外观
yaml
原文: https://finproducts.atlassian.net/wiki/spaces/JIDD3/pages/34793947336/FIN+5.3.1+yaml 中文整理基于 FIN 5.3.1
导读: Fantom 的 YAML 解析器
以下为 API 参考,保留英文以便对照源码;目录已列出(英文标题便于检索);概述已尽量中译。
目录(导读)
- classes
- docs
- YAML
- Reading YAML documents
- Writing YAML documents
- Schemas
- Slot Details
- each
- get
- make
- val
- decode
- equals
- hash
- loc
- tag
- toStr
- write
- makeFile
- parse
- core
- encode
- failsafe
- json
API 正文(英文)
YAML
This pod provides the means for parsing YAML files into native Fantom objects, as well as transforming Fantom objects back into YAML text. For information about YAML, see https://yaml.org/.
To do this, the intermediate class YamlObj is used, representing an abstract YAML node. Each node has a type (YamlScalar, YamlList, or YamlMap), a tag (e.g. tag:yaml.org,2002:str or ? for an non-specific tag), and content (which, depending on the node type, could contain further nodes). This can then be converted to a YAML string or a Fantom object.
This parser is fully compliant with the YAML 1.2.2 specification, other than disallowing nodes which contain themselves.
Reading YAML documents
First, YamlReader.parse parses YAML text from a stream into a YamlObj hierarchy. Then, YamlObj.decode finishes the process, transforming the YamlObj into a native Fantom object. (See the schema section on how to fine-tune this process if desired.)
Since multiple documents may be contained within a single YAML stream, the result of YamlReader.parse is always a YamlList tagged with tag:yaml.org,2002:stream which contains the list of documents as content. This way, you may call YamlObj.decode directly on the result, and it will correctly output the list of documents as a list of Fantom objects.
str := "---
- This is
- a list!
- which is in a different
document from...
---
foo: 1
map:
inner list: [a,b,c] # comment
"
res := YamlReader(str.in).parse.decode
//output
[
["This is", "a list!", "which is in a different document from..."],
["foo": 1, "map": null, "inner list": ["a","b","c"]]
]Writing YAML documents
This time, the process occurs in reverse: native Fantom objects can be converted to YamlObjs, which can then be written to text.
To convert a Fantom object to a YamlObj, use YamlSchema.encode with a schema of your choice (YamlSchema.core is recommended). Then, to convert that YamlObj to text, call YamlObj.write:
map := ["one key": [1, 2, 3], "multiline\nkey": [:], ["complex", "key"].toImmutable: false]
yamlObj := YamlSchema.core.encode(map)
yamlObj.write
//written to standard out
? - complex
- key
: false
"multiline\nkey": {}
one key:
- 1
- 2
- 3You may also use YamlObj.toStr to write this content into a string rather than an OutStream. Note that all objects written either way end with a newline character.
Also note that any serializable object can be encoded in YAML:
- A simple is just encoded as a string.
- A complex is encoded as a map, where each field is a map entry.
- A collection is encoded like a complex, except there is an additional
eachfield that contains the child items.
The object as a whole is then tagged as !fan/pod::Type.
Finally, note that this will always use YAML's block style to write objects, as it is not possible for some YAML objects to be written in flow style and still be preserved. If you would like to write objects in flow style anyway, use JsonOutStream.
Schemas
Often, YAML documents contain plain, untagged text, such as Text or 1. (This is represented in YamlObjs by the ? tag.) Sometimes, we want these to be parsed as something other than a Str; for example, we might want 1 to be parsed as an Int, true as a Bool, and so on.
This is where the YamlSchema class comes in. Schemas are all about assigning and reading tags. Different schemas may assign different tags to ?-tagged nodes (reading) or Fantom objects (writing), and may treat tagged nodes differently when creating Fantom objects.
This lists the differences between the built-in schemas (where !! is short for tag:yaml.org,2002:):
+-------------+--------------------------+-------------------------------------+
| Schema | Recognized tags => types | Main uses |
|-------------+--------------------------+-------------------------------------|
| YamlSchema. | !!str => Str | Useful when you want all text to be |
| failsafe | !!seq => List | treated literally. For example, |
| | !!map => Map | empty keys will be treated as "" |
| | | instead of null. |
|-------------+--------------------------+-------------------------------------|
| YamlSchema. | failsafe's tags, + | Useful if you want to parse JSON- |
| json | !!null => null | like text that may be mixed with |
| | !!bool => Bool | YAML's compact styles. Though it |
| | !!int => Int | won't notice anything style- |
| | !!float => Float | related, it will error if there |
| | | are plain, untagged strings that |
| | | are not null/booleans/ints/floats. |
|-------------+--------------------------+-------------------------------------|
| YamlSchema. | json's tags | Useful in most cases (and thus |
| core | | used as the default schema). A wide |
| | | variety of patterns can be used |
| | | to indicate non-Str content (e.g. |
| | | ints can be specified in hex or |
| | | octal). Unlike the JsonSchema, the |
| | | CoreSchema accepts plain strings. |
+-------------+--------------------------+-------------------------------------+See https://yaml.org/spec/1.2.2/#recommended-schemas for more information about each schema and exact definitions of each pattern.
Examples:
str := "int: 1
empty:
plain: Text
large float: -2E+05
"
parsed := YamlReader(str.in).parse
failsafe := YamlSchema.failsafe.decode(parsed)
core := YamlSchema.core.decode(parsed)
//failsafe content - text is just literal
[
["int": "1", "empty": "", "plain": "Text", "large float": "-2E+05"]
]
//core content - text is interpreted in different ways
[
["int": 1, "empty": null, "plain": "Text", "large float": -20000.0f]
]Creating custom schemas
To interpret additional tags, a custom schema must be created. To do this, it is recommended that you subclass the CoreSchema class (which is itself a subclass of YamlSchema), adding the extra tags you want from there. This makes it possible to keep all of the core schema's tags in use (e.g. 0x05 is still interpreted as the Int 5 without further work needed).
YamlSchemas all have three protected helper methods that you inherit: assignTag, validate, and isRecognized. The first is meant to be used for ?-tagged nodes, giving the node a proper tag, while the second is meant to be used for nodes with specified tags, ensuring that a !!int- tagged node does not contain "true" as content instead of, say, "5". The third just returns true if the schema recognizes a given tag. In each of these methods, if super.isRecognized would return true for a node (or super.assignTag assigns something other than !!str), you should use the superclass's method before defaulting to your own code; e.g. if a node contains the content true, calling super.assignTag will correctly return the !!bool tag. See the implementations of the JSON and core schemas in YamlSchema.fan for examples of this, both which inherit from the failsafe schema.
YamlList
const class YamlList : YamlObj
A YamlObj whose content always has the type YamlObj[]. For example, each item on the list below is itself a list:
- - a
- b
- c
- [a, b, c]Constructors
| make | Creates a YamlList with the list val as content, found at location loc, with tag as its tag. |
Methods
| each | Iterate the list items |
| get | Get item from the list |
| val | Content value as a list |
Slot Details
each
Void each(|YamlObj| f)
Iterate the list items
get
@Operator YamlObj get(Int index)
Get item from the list
make
new make(YamlObj[] val, Str tag := "!", FileLoc loc := FileLoc.unknown)
Creates a YamlList with the list val as content, found at location loc, with tag as its tag.
val
virtual override YamlObj[] val()
Content value as a list
YamlMap
const class YamlMap : YamlObj
A YamlObj whose content always has the type YamlObj:YamlObj. For example, each item on the list below is a map:
- foo: bar
a: b
- {foo: bar, a: b}Constructors
| make | Creates a YamlMap with the map val as content, found at location loc, with tag as its tag. |
Methods
| val | Content value as a map |
Slot Details
make
new make(YamlObj:YamlObj val, Str tag := "!", FileLoc loc := FileLoc.unknown)
Creates a YamlMap with the map val as content, found at location loc, with tag as its tag.
val
virtual override YamlObj:YamlObj val()
Content value as a map
YamlObj
@Serializable abstract const class YamlObj : Obj
The base class for objects that represent nodes in a YAML hierarchy. The key information for each node is its tag and content.
See the pod documentation for more information.
Methods
| decode | Convenience for schema.decode. |
| equals | Two YamlObjs are equal if they have the same type, same tag, and same content. |
| hash | Hash is based on tag and content |
| loc | The text location from which this node was parsed. |
| tag | The node's tag. |
| toStr | Returns write written into a string. |
| val | The node's content value. |
| write | Transforms the YAML object back into a string, using block style where applicable. |
Slot Details
decode
Obj? decode(YamlSchema schema := YamlSchema.core)
Convenience for schema.decode.
equals
virtual override Bool equals(Obj? that)
Two YamlObjs are equal if they have the same type, same tag, and same content.
hash
virtual override Int hash()
Hash is based on tag and content
loc
FileLoc loc()
The text location from which this node was parsed.
tag
Str tag()
The node's tag. Either a specific tag (e.g. tag:yaml.org,2002:str) or the non-specific tag ?.
toStr
virtual override Str toStr()
Returns write written into a string.
val
virtual Obj val()
The node's content value. YamlScalars always have content of type Str, YamlLists with content type YamlObj[], and YamlMaps with YamlObj:YamlObj.
write
Void write(OutStream out := Env.cur().out())
Transforms the YAML object back into a string, using block style where applicable. The result ends with \n and may span multiple lines.
YamlReader
class YamlReader : Obj
Converts YAML text into a hierarchy of YamlObjs, which can in turn be converted into native Fantom objects.
See the pod documentation for more information.
Constructors
| make | Creates a new YamlReader that reads YAML content from the given InStream, optionally tracking file location data. |
| makeFile | Creates a new YamlReader that reads YAML content from the given file. |
Methods
| parse | Parses the input stream as YAML content, returning each document as its own YamlObj within a larger YamlList. |
Slot Details
make
new make(InStream in, FileLoc loc := FileLoc.unknown)
Creates a new YamlReader that reads YAML content from the given InStream, optionally tracking file location data.
makeFile
new makeFile(File file)
Creates a new YamlReader that reads YAML content from the given file.
parse
YamlList parse()
Parses the input stream as YAML content, returning each document as its own YamlObj within a larger YamlList.
YamlScalar
const class YamlScalar : YamlObj
A YamlObj whose content always has the type Str. For example, each item on the list below is a scalar:
- This is a plain scalar
- "This is a string"
- !!int 5Constructors
| make | Creates a YamlScalar with the string val as content, found at location loc, with tag as its tag. |
Methods
| val | Content value as a string |
Slot Details
make
new make(Str val, Str tag := "?", FileLoc loc := FileLoc.unknown)
Creates a YamlScalar with the string val as content, found at location loc, with tag as its tag.
val
virtual override Str val()
Content value as a string
YamlSchema
abstract const class YamlSchema : Obj
A class used to convert parsed YamlObjs to native Fantom objects, and vice versa.
Different schemas can recognize different tags; for example, the failsafe schema only recognizes the str, seq, and map tags and ignores all the rest (thus only generating Strs, Lists, and Maps), while the JSON schema also recognizes integers, booleans, etc.
See the pod documentation for more information, especially about the specifics of each built-in schema.
Fields
| core | The default YamlSchema. |
| failsafe | A basic YamlSchema that only generates maps, lists, and strings. |
| json | A YamlSchema that can parse all JSON tokens and errors when a plain token is not JSON-compliant. |
Methods
| decode | Recursively transforms the YAML object into a native Fantom object using this tag resolution schema. |
| encode | Transforms the native Fantom object into a YamlObj hierarchy that preserves as much information as possible. |
Slot Details
core
const static YamlSchema core
The default YamlSchema.
decode
virtual Obj? decode(YamlObj node)
Recursively transforms the YAML object into a native Fantom object using this tag resolution schema.
encode
abstract YamlObj encode(Obj? obj)
Transforms the native Fantom object into a YamlObj hierarchy that preserves as much information as possible. The object must be serializable or simple. YamlObjs are encoded as themselves; no further processing is done.
failsafe
const static YamlSchema failsafe
A basic YamlSchema that only generates maps, lists, and strings.
json
const static YamlSchema json
A YamlSchema that can parse all JSON tokens and errors when a plain token is not JSON-compliant.