2022.06.17
流风格7.1. Alias Nodes7.2. Empty Nodes7.3. Flow Scalar Styles7.3.1. Double-Quoted Style7.3.2. Single-Quoted Style7.3.3. Plain Style7.4. Flow Collection Styles7.4.1. Flow Sequences7.4.2. Flow Mappings7.5. Flow Nodes
本文按照YAML官方文档1.2.2版本翻译总结而成
https://yaml.org/spec/1.2.2/#chapter-7-flow-style-productions
YAML’s flow styles can be thought of as the natural extension of JSON to cover folding long content lines for readability, tagging nodes to control construction of native data structures and using anchors and aliases to reuse constructed object instances.
Subsequent occurrences of a previously serialized node are presented as alias nodes. The first occurrence of the node must be marked by an anchor to allow subsequent occurrences to be presented as alias nodes.
An alias node is denoted by the “*
” indicator. The alias refers to the most recent preceding node having the same anchor. It is an error for an alias node to use an anchor that does not previously occur in the document. It is not an error to specify an anchor that is not used by any alias node.
Note that an alias node must not specify any properties or content, as these were already specified at the first occurrence of the node.
[104] c-ns-alias-node ::=
c-alias # '*'
ns-anchor-name
Example 7.1 Alias Nodes
First occurrence: &anchor Foo Second occurrence: *anchor Override anchor: &anchor Bar Reuse anchor: *anchor | { "First occurrence": &A "Foo", "Override anchor": &B "Bar", "Second occurrence": *A, "Reuse anchor": *B } |
---|---|
Legend:
c-ns-alias-node
ns-anchor-name
YAML allows the node content to be omitted in many cases. Nodes with empty content are interpreted as if they were plain scalars with an empty value. Such nodes are commonly resolved to a “null
” value.
[105] e-scalar ::= ""
In the examples, empty scalars are sometimes displayed as the glyph “°
” for clarity. Note that this glyph corresponds to a position in the characters stream rather than to an actual character.
Example 7.2 Empty Content
{ foo : !!str°, !!str° : bar, } | { "foo": "", "": "bar" } |
---|---|
Legend:
e-scalar
Both the node’s properties and node content are optional. This allows for a completely empty node. Completely empty nodes are only valid when following some explicit indication for their existence.
[106] e-node ::=
e-scalar # ""
Example 7.3 Completely Empty Flow Nodes
{ ? foo :°, °: bar, } | { "foo": null, null : "bar" } |
---|---|
Legend:
e-node
YAML provides three flow scalar styles: double-quoted, single-quoted and plain (unquoted). Each provides a different trade-off between readability and expressive power.
The scalar style is a presentation detail and must not be used to convey content information, with the exception that plain scalars are distinguished for the purpose of tag resolution.
The double-quoted style is specified by surrounding “"
” indicators. This is the only style capable of expressing arbitrary strings, by using “\
” escape sequences. This comes at the cost of having to escape the “\
” and “"
” characters.
[107] nb-double-char ::=
c-ns-esc-char
| (
nb-json
- c-escape # '\'
- c-double-quote # '"'
)
[108] ns-double-char ::=
nb-double-char - s-white
Double-quoted scalars are restricted to a single line when contained inside an implicit key.
[109] c-double-quoted(n,c) ::=
c-double-quote # '"'
nb-double-text(n,c)
c-double-quote # '"'
[110]
nb-double-text(n,FLOW-OUT) ::= nb-double-multi-line(n)
nb-double-text(n,FLOW-IN) ::= nb-double-multi-line(n)
nb-double-text(n,BLOCK-KEY) ::= nb-double-one-line
nb-double-text(n,FLOW-KEY) ::= nb-double-one-line
[111] nb-double-one-line ::=
nb-double-char*
Example 7.4 Double Quoted Implicit Keys
"implicit block key" : [ "implicit flow key" : value, ] | { "implicit block key": [ { "implicit flow key": "value" } ] } |
---|---|
Legend:
nb-double-one-line
c-double-quoted(n,c)
In a multi-line double-quoted scalar, line breaks are subject to flow line folding, which discards any trailing white space characters. It is also possible to escape the line break character. In this case, the escaped line break is excluded from the content and any trailing white space characters that precede the escaped line break are preserved. Combined with the ability to escape white space characters, this allows double-quoted lines to be broken at arbitrary positions.
[112] s-double-escaped(n) ::=
s-white*
c-escape # '\'
b-non-content
l-empty(n,FLOW-IN)*
s-flow-line-prefix(n)
[113] s-double-break(n) ::=
s-double-escaped(n)
| s-flow-folded(n)
Example 7.5 Double Quoted Line Breaks
"folded·↓ to a space,→↓ ·↓ to a line feed, or·→\↓ ·\·→non-content" | "folded to a space,\nto a line feed, or \t \tnon-content" |
---|---|
Legend:
s-flow-folded(n)
s-double-escaped(n)
All leading and trailing white space characters on each line are excluded from the content. Each continuation line must therefore contain at least one non-space character. Empty lines, if any, are consumed as part of the line folding.
[114] nb-ns-double-in-line ::=
(
s-white*
ns-double-char
)*
[115] s-double-next-line(n) ::=
s-double-break(n)
(
ns-double-char nb-ns-double-in-line
(
s-double-next-line(n)
| s-white*
)
)?
[116] nb-double-multi-line(n) ::=
nb-ns-double-in-line
(
s-double-next-line(n)
| s-white*
)
Example 7.6 Double Quoted Lines
"·1st non-empty↓ ↓ ·2nd non-empty· →3rd non-empty·" | " 1st non-empty\n2nd non-empty 3rd non-empty " |
---|---|
Legend:
nb-ns-double-in-line
s-double-next-line(n)
The single-quoted style is specified by surrounding “'
” indicators. Therefore, within a single-quoted scalar, such characters need to be repeated. This is the only form of escaping performed in single-quoted scalars. In particular, the “\
” and “"
” characters may be freely used. This restricts single-quoted scalars to printable characters. In addition, it is only possible to break a long single-quoted line where a space character is surrounded by non-spaces.
[117] c-quoted-quote ::= "''"
[118] nb-single-char ::=
c-quoted-quote
| (
nb-json
- c-single-quote # "'"
)
[119] ns-single-char ::=
nb-single-char - s-white
Example 7.7 Single Quoted Characters
'here''s to "quotes"' | "here's to \"quotes\"" |
---|---|
Legend:
c-quoted-quote
Single-quoted scalars are restricted to a single line when contained inside a implicit key.
[120] c-single-quoted(n,c) ::=
c-single-quote # "'"
nb-single-text(n,c)
c-single-quote # "'"
[121]
nb-single-text(FLOW-OUT) ::= nb-single-multi-line(n)
nb-single-text(FLOW-IN) ::= nb-single-multi-line(n)
nb-single-text(BLOCK-KEY) ::= nb-single-one-line
nb-single-text(FLOW-KEY) ::= nb-single-one-line
[122] nb-single-one-line ::=
nb-single-char*
Example 7.8 Single Quoted Implicit Keys
'implicit block key' : [ 'implicit flow key' : value, ] | { "implicit block key": [ { "implicit flow key": "value" } ] } |
---|---|
Legend:
nb-single-one-line
c-single-quoted(n,c)
All leading and trailing white space characters are excluded from the content. Each continuation line must therefore contain at least one non-space character. Empty lines, if any, are consumed as part of the line folding.
[123] nb-ns-single-in-line ::=
(
s-white*
ns-single-char
)*
[124] s-single-next-line(n) ::=
s-flow-folded(n)
(
ns-single-char
nb-ns-single-in-line
(
s-single-next-line(n)
| s-white*
)
)?
[125] nb-single-multi-line(n) ::=
nb-ns-single-in-line
(
s-single-next-line(n)
| s-white*
)
Example 7.9 Single Quoted Lines
'·1st non-empty↓ ↓ ·2nd non-empty· →3rd non-empty·' | " 1st non-empty\n2nd non-empty 3rd non-empty " |
---|---|
Legend:
nb-ns-single-in-line(n)
s-single-next-line(n)
The plain (unquoted) style has no identifying indicators and provides no form of escaping. It is therefore the most readable, most limited and most context sensitive style. In addition to a restricted character set, a plain scalar must not be empty or contain leading or trailing white space characters. It is only possible to break a long plain line where a space character is surrounded by non-spaces.
Plain scalars must not begin with most indicators, as this would cause ambiguity with other YAML constructs. However, the “:
”, “?
” and “-
” indicators may be used as the first character if followed by a non-space “safe” character, as this causes no ambiguity.
[126] ns-plain-first(c) ::=
(
ns-char
- c-indicator
)
| (
(
c-mapping-key # '?'
| c-mapping-value # ':'
| c-sequence-entry # '-'
)
[ lookahead = ns-plain-safe(c) ]
)
Plain scalars must never contain the “:
” and “#
” character combinations. Such combinations would cause ambiguity with mapping key/value pairs and comments. In addition, inside flow collections, or when used as implicit keys, plain scalars must not contain the “[
”, “]
”, “{
”, “}
” and “,
” characters. These characters would cause ambiguity with flow collection structures.
[127]
ns-plain-safe(FLOW-OUT) ::= ns-plain-safe-out
ns-plain-safe(FLOW-IN) ::= ns-plain-safe-in
ns-plain-safe(BLOCK-KEY) ::= ns-plain-safe-out
ns-plain-safe(FLOW-KEY) ::= ns-plain-safe-in
[128] ns-plain-safe-out ::=
ns-char
[129] ns-plain-safe-in ::=
ns-char - c-flow-indicator
[130] ns-plain-char(c) ::=
(
ns-plain-safe(c)
- c-mapping-value # ':'
- c-comment # '#'
)
| (
[ lookbehind = ns-char ]
c-comment # '#'
)
| (
c-mapping-value # ':'
[ lookahead = ns-plain-safe(c) ]
)
Example 7.10 Plain Characters
# Outside flow collection: - ::vector - ": - ()" - Up, up, and away! - -123 - https://example.com/foo#bar # Inside flow collection: - [ ::vector, ": - ()", "Up, up and away!", -123, https://example.com/foo#bar ] | [ "::vector", ": - ()", "Up, up, and away!", -123, "http://example.com/foo#bar", [ "::vector", ": - ()", "Up, up, and away!", -123, "http://example.com/foo#bar" ] ] |
---|---|
Legend:
ns-plain-first(c)
ns-plain-char(c)
Not ns-plain-first(c)
Not ns-plain-char(c)
Plain scalars are further restricted to a single line when contained inside an implicit key.
[131]
ns-plain(n,FLOW-OUT) ::= ns-plain-multi-line(n,FLOW-OUT)
ns-plain(n,FLOW-IN) ::= ns-plain-multi-line(n,FLOW-IN)
ns-plain(n,BLOCK-KEY) ::= ns-plain-one-line(BLOCK-KEY)
ns-plain(n,FLOW-KEY) ::= ns-plain-one-line(FLOW-KEY)
[132] nb-ns-plain-in-line(c) ::=
(
s-white*
ns-plain-char(c)
)*
[133] ns-plain-one-line(c) ::=
ns-plain-first(c)
nb-ns-plain-in-line(c)
Example 7.11 Plain Implicit Keys
implicit block key : [ implicit flow key : value, ] | { "implicit block key": [ { "implicit flow key": "value" } ] } |
---|---|
Legend:
ns-plain-one-line(c)
All leading and trailing white space characters are excluded from the content. Each continuation line must therefore contain at least one non-space character. Empty lines, if any, are consumed as part of the line folding.
[134] s-ns-plain-next-line(n,c) ::=
s-flow-folded(n)
ns-plain-char(c)
nb-ns-plain-in-line(c)
[135] ns-plain-multi-line(n,c) ::=
ns-plain-one-line(c)
s-ns-plain-next-line(n,c)*
Example 7.12 Plain Lines
1st non-empty↓ ↓ ·2nd non-empty· →3rd non-empty | "1st non-empty\n2nd non-empty 3rd non-empty" |
---|---|
Legend:
nb-ns-plain-in-line(c)
s-ns-plain-next-line(n,c)
A flow collection may be nested within a block collection ([FLOW-OUT
context]), nested within another flow collection ([FLOW-IN
context]) or be a part of an implicit key ([FLOW-KEY
context] or [BLOCK-KEY
context]). Flow collection entries are terminated by the “,
” indicator. The final “,
” may be omitted. This does not cause ambiguity because flow collection entries can never be completely empty.
[136]
in-flow(n,FLOW-OUT) ::= ns-s-flow-seq-entries(n,FLOW-IN)
in-flow(n,FLOW-IN) ::= ns-s-flow-seq-entries(n,FLOW-IN)
in-flow(n,BLOCK-KEY) ::= ns-s-flow-seq-entries(n,FLOW-KEY)
in-flow(n,FLOW-KEY) ::= ns-s-flow-seq-entries(n,FLOW-KEY)
Flow sequence content is denoted by surrounding “[
” and “]
” characters.
[137] c-flow-sequence(n,c) ::=
c-sequence-start # '['
s-separate(n,c)?
in-flow(n,c)?
c-sequence-end # ']'
Sequence entries are separated by a “,
” character.
[138] ns-s-flow-seq-entries(n,c) ::=
ns-flow-seq-entry(n,c)
s-separate(n,c)?
(
c-collect-entry # ','
s-separate(n,c)?
ns-s-flow-seq-entries(n,c)?
)?
Example 7.13 Flow Sequence
- [ one, two, ] - [three ,four] | [ [ "one", "two" ], [ "three", "four" ] ] |
---|---|
Legend:
c-sequence-start c-sequence-end
ns-flow-seq-entry(n,c)
Any flow node may be used as a flow sequence entry. In addition, YAML provides a compact notation for the case where a flow sequence entry is a mapping with a single key/value pair.
[139] ns-flow-seq-entry(n,c) ::=
ns-flow-pair(n,c) | ns-flow-node(n,c)
Example 7.14 Flow Sequence Entries
[ "double quoted", 'single quoted', plain text, [ nested ], single: pair, ] | [ "double quoted", "single quoted", "plain text", [ "nested" ], { "single": "pair" } ] |
---|---|
Legend:
ns-flow-node(n,c)
ns-flow-pair(n,c)
Flow mappings are denoted by surrounding “{
” and “}
” characters.
[140] c-flow-mapping(n,c) ::=
c-mapping-start # '{'
s-separate(n,c)?
ns-s-flow-map-entries(n,in-flow(c))?
c-mapping-end # '}'
Mapping entries are separated by a “,
” character.
[141] ns-s-flow-map-entries(n,c) ::=
ns-flow-map-entry(n,c)
s-separate(n,c)?
(
c-collect-entry # ','
s-separate(n,c)?
ns-s-flow-map-entries(n,c)?
)?
Example 7.15 Flow Mappings
- { one : two , three: four , } - {five: six,seven : eight} | [ { "one": "two", "three": "four" }, { "five": "six", "seven": "eight" } ] |
---|---|
Legend:
c-mapping-start c-mapping-end
ns-flow-map-entry(n,c)
If the optional “?
” mapping key indicator is specified, the rest of the entry may be completely empty.
[142] ns-flow-map-entry(n,c) ::=
(
c-mapping-key # '?' (not followed by non-ws char)
s-separate(n,c)
ns-flow-map-explicit-entry(n,c)
)
| ns-flow-map-implicit-entry(n,c)
[143] ns-flow-map-explicit-entry(n,c) ::=
ns-flow-map-implicit-entry(n,c)
| (
e-node # ""
e-node # ""
)
Example 7.16 Flow Mapping Entries
{ ? explicit: entry, implicit: entry, ?°° } | { "explicit": "entry", "implicit": "entry", null: null } |
---|---|
Legend:
ns-flow-map-explicit-entry(n,c)
ns-flow-map-implicit-entry(n,c)
e-node
Normally, YAML insists the “:
” mapping value indicator be separated from the value by white space. A benefit of this restriction is that the “:
” character can be used inside plain scalars, as long as it is not followed by white space. This allows for unquoted URLs and timestamps. It is also a potential source for confusion as “a:1
” is a plain scalar and not a key/value pair.
Note that the value may be completely empty since its existence is indicated by the “:
”.
[144] ns-flow-map-implicit-entry(n,c) ::=
ns-flow-map-yaml-key-entry(n,c)
| c-ns-flow-map-empty-key-entry(n,c)
| c-ns-flow-map-json-key-entry(n,c)
[145] ns-flow-map-yaml-key-entry(n,c) ::=
ns-flow-yaml-node(n,c)
(
(
s-separate(n,c)?
c-ns-flow-map-separate-value(n,c)
)
| e-node # ""
)
[146] c-ns-flow-map-empty-key-entry(n,c) ::=
e-node # ""
c-ns-flow-map-separate-value(n,c)
[147] c-ns-flow-map-separate-value(n,c) ::=
c-mapping-value # ':'
[ lookahead ≠ ns-plain-safe(c) ]
(
(
s-separate(n,c)
ns-flow-node(n,c)
)
| e-node # ""
)
Example 7.17 Flow Mapping Separate Values
{ unquoted·:·"separate", https://foo.com, omitted value:°, °:·omitted key, } | { "unquoted": "separate", "http://foo.com": null, "omitted value": null, null: "omitted key" } |
---|---|
Legend:
ns-flow-yaml-node(n,c)
e-node
c-ns-flow-map-separate-value(n,c)
To ensure JSON compatibility, if a key inside a flow mapping is JSON-like, YAML allows the following value to be specified adjacent to the “:
”. This causes no ambiguity, as all JSON-like keys are surrounded by indicators. However, as this greatly reduces readability, YAML processors should separate the value from the “:
” on output, even in this case.
[148] c-ns-flow-map-json-key-entry(n,c) ::=
c-flow-json-node(n,c)
(
(
s-separate(n,c)?
c-ns-flow-map-adjacent-value(n,c)
)
| e-node # ""
)
[149] c-ns-flow-map-adjacent-value(n,c) ::=
c-mapping-value # ':'
(
(
s-separate(n,c)?
ns-flow-node(n,c)
)
| e-node # ""
)
Example 7.18 Flow Mapping Adjacent Values
{ "adjacent":value, "readable":·value, "empty":° } | { "adjacent": "value", "readable": "value", "empty": null } |
---|---|
Legend:
c-flow-json-node(n,c)
e-node
c-ns-flow-map-adjacent-value(n,c)
A more compact notation is usable inside flow sequences, if the mapping contains a single key/value pair. This notation does not require the surrounding “{
” and “}
” characters. Note that it is not possible to specify any node properties for the mapping in this case.
Example 7.19 Single Pair Flow Mappings
[ foo: bar ] | [ { "foo": "bar" } ] |
---|---|
Legend:
ns-flow-pair(n,c)
If the “?
” indicator is explicitly specified, parsing is unambiguous and the syntax is identical to the general case.
[150] ns-flow-pair(n,c) ::=
(
c-mapping-key # '?' (not followed by non-ws char)
s-separate(n,c)
ns-flow-map-explicit-entry(n,c)
)
| ns-flow-pair-entry(n,c)
Example 7.20 Single Pair Explicit Entry
[ ? foo bar : baz ] | [ { "foo bar": "baz" } ] |
---|---|
Legend:
ns-flow-map-explicit-entry(n,c)
If the “?
” indicator is omitted, parsing needs to see past the implicit key to recognize it as such. To limit the amount of lookahead required, the “:
” indicator must appear at most 1024 Unicode characters beyond the start of the key. In addition, the key is restricted to a single line.
Note that YAML allows arbitrary nodes to be used as keys. In particular, a key may be a sequence or a mapping. Thus, without the above restrictions, practical one-pass parsing would have been impossible to implement.
[151] ns-flow-pair-entry(n,c) ::=
ns-flow-pair-yaml-key-entry(n,c)
| c-ns-flow-map-empty-key-entry(n,c)
| c-ns-flow-pair-json-key-entry(n,c)
[152] ns-flow-pair-yaml-key-entry(n,c) ::=
ns-s-implicit-yaml-key(FLOW-KEY)
c-ns-flow-map-separate-value(n,c)
[153] c-ns-flow-pair-json-key-entry(n,c) ::=
c-s-implicit-json-key(FLOW-KEY)
c-ns-flow-map-adjacent-value(n,c)
[154] ns-s-implicit-yaml-key(c) ::=
ns-flow-yaml-node(0,c)
s-separate-in-line?
/* At most 1024 characters altogether */
[155] c-s-implicit-json-key(c) ::=
c-flow-json-node(0,c)
s-separate-in-line?
/* At most 1024 characters altogether */
Example 7.21 Single Pair Implicit Entries
- [ YAML·: separate ] - [ °: empty key entry ] - [ {JSON: like}:adjacent ] | [ [ { "YAML": "separate" } ], [ { null: "empty key entry" } ], [ { { "JSON": "like" }: "adjacent" } ] ] |
---|---|
Legend:
ns-s-implicit-yaml-key
e-node
c-s-implicit-json-key
Value
Example 7.22 Invalid Implicit Keys
[ foo bar: invalid, "foo_...>1K characters..._bar": invalid ] | ERROR: - The foo bar key spans multiple lines - The foo...bar key is too long |
---|---|
JSON-like flow styles all have explicit start and end indicators. The only flow style that does not have this property is the plain scalar. Note that none of the “JSON-like” styles is actually acceptable by JSON. Even the double-quoted style is a superset of the JSON string format.
[156] ns-flow-yaml-content(n,c) ::=
ns-plain(n,c)
[157] c-flow-json-content(n,c) ::=
c-flow-sequence(n,c)
| c-flow-mapping(n,c)
| c-single-quoted(n,c)
| c-double-quoted(n,c)
[158] ns-flow-content(n,c) ::=
ns-flow-yaml-content(n,c)
| c-flow-json-content(n,c)
Example 7.23 Flow Content
- [ a, b ] - { a: b } - "a" - 'b' - c | [ [ "a", "b" ], { "a": "b" }, "a", "b", "c" ] |
---|---|
Legend:
c-flow-json-content(n,c)
ns-flow-yaml-content(n,c)
A complete flow node also has optional node properties, except for alias nodes which refer to the anchored node properties.
[159] ns-flow-yaml-node(n,c) ::=
c-ns-alias-node
| ns-flow-yaml-content(n,c)
| (
c-ns-properties(n,c)
(
(
s-separate(n,c)
ns-flow-yaml-content(n,c)
)
| e-scalar
)
)
[160] c-flow-json-node(n,c) ::=
(
c-ns-properties(n,c)
s-separate(n,c)
)?
c-flow-json-content(n,c)
[161] ns-flow-node(n,c) ::=
c-ns-alias-node
| ns-flow-content(n,c)
| (
c-ns-properties(n,c)
(
(
s-separate(n,c)
ns-flow-content(n,c)
)
| e-scalar
)
)
Example 7.24 Flow Nodes
- !!str "a" - 'b' - &anchor "c" - *anchor - !!str° | [ "a", "b", "c", "c", "" ] |
---|---|
Legend:
c-flow-json-node(n,c)
ns-flow-yaml-node(n,c)