System.Yaml.YamlNode

日本語版はこちら

YamlNode is the abstract base class of YAML data nodes.

Actual data nodes are represented by YamlScalar, YamlSequence and YamlMapping.

YAML data model

See http://yaml.org/ for the official definition of the Information Models of YAML.

YAML data structure is defined as follows. Note that this does not represents the text syntax of YAML text but does logical data structure.
  • yaml-stream ::= yaml-document*
  • yaml-document ::= yaml-directive* yaml-node
  • yaml-directive ::= YAML-directive | TAG-directive | user-defined-directive
  • yaml-node ::= yaml-scalar | yaml-sequence | yaml-mapping
  • yaml-scalar ::= yaml-tag yaml-value
  • yaml-sequence ::= yaml-tag yaml-node*
  • yaml-mapping ::= yaml-tag ( yaml-node yaml-node )*
  • yaml-tag ::= yaml-global-tag yaml-local-tag
  • yaml-global-tag ::= "tag:" taggingEntity ":" specific [ "#" fragment ]
  • yaml-local-tag ::= "!" yaml-local-tag-name

Namely,
  • A YAML stream consists of zero or more YAML documents.
  • A YAML documents have zero or more YAML directives and a root YAML node.
  • A YAML directive is either YAML-directive, TAG-directive or user-defined-directive.
  • A YAML node is either YAML scalar, YAML sequence or YAML mapping.
  • A YAML scalar consists of a YAML tag and a scalar value.
  • A YAML sequence consists of a YAML tag and zero or more child YAML nodes.
  • A YAML mapping cosists of a YAML tag and zero or more key/value pairs of YAML nodes.
  • A YAML tag is either a YAML global tag or a YAML local tag.
  • A YAML global tag starts with "tag:" and described in the "tag:" URI scheme defined in RFC4151.
  • A YAML local tag starts with "!" with a YAML local tag name

// Construct YAML node tree
YamlNode node = 
    new YamlSequence(                           // !!seq node
        new YamlScalar("abc"),                  // !!str node
        new YamlScalar("!!int", "123"),         // !!int node
        new YamlScalar("!!float", "1.23"),      // !!float node
        new YamlSequence(                       // nesting !!seq node
            new YamlScalar("def"),
            new YamlScalar("ghi")
        ),
        new YamlMapping(                        // !!map node
            new YamlScalar("key1"), new YamlScalar("value1"),
            new YamlScalar("key2"), new YamlScalar("value2"),
            new YamlScalar("key3"), new YamlMapping(    // nesting !!map node
                new YamlScalar("value3key1"), new YamlScalar("value3value1")
            ),
            new YamlScalar("key4"), new YamlScalar("value4")
        )
    );

// Convert it to YAML stream
string yaml = node.ToYaml();

// %YAML 1.2
// ---
// - abc
// - 123
// - 1.23
// - - def
//   - ghi
// - key1: value1
//   key2: value2
//   key3:
//     value3key1: value3value1
//   key4: value4
// ...

// Load the YAML node from the YAML stream.
// Note that a YAML stream can contain several YAML documents each of which
// contains a root YAML node.
YamlNode[] nodes = YamlNode.FromYaml(yaml);

// The only one node in the stream is the one we have presented above.
Assert.AreEqual(1, nodes.Length);
YamlNode resotred = nodes[0];

// Check if they are equal to each other.
Assert.AreEquel(node, restored);

// Extract sub nodes.
var seq = (YamlSequence)restored;
var map = (YamlMapping)seq[4];
var map2 = (YamlMapping)map[new YamlScalar("key3")];

// Modify the restored node tree
map2[new YamlScalar("value3key1")] = new YamlScalar("value3value1 modified");

// Now they are not equal to each other.
Assert.AreNotEquel(node, restored);

YamlNode class

YamlNode is an abstract base class that represents a YAML node.

Actual data nodes are represented by YamlScalar, YamlSequence and YamlMapping.

All YamlNode has Tag property that denotes the data type represented as the YAML node.

Default Tag value for YamlScalar, YamlSequence and YamlMapping are "tag:yaml.org,2002:str", "tag:yaml.org,2002:seq" and "tag:yaml.org,2002:map". Global tags that starts with "tag:yaml.org,2002:" ( = YamlNode.DefaultTagPrefix) are defined in the YAML tag repository at http://yaml.org/type/.

In this library, such a tags can also be represented in a short hand form that starts with "!!", like "!!str", "!!seq" and "!!map". Tags in the formal style and the shorthand form can be converted to each other by the static methods of ExpandTag(string) and ShorthandTag(string). In addition to these three basic tags, this library supports "!!null", "!!bool", "!!int", "!!float" and "!!timestamp" tags, by default.

YamlNodes can be read from a YAML stream with FromYaml(string), FromYaml(Stream), FromYaml(TextReader) and FromYamlFile(string) static methods. Since a YAML stream generally consists of multiple YAML documents, each of which has one root YAML node, these methods return an array of YamlNode that is contained in the stream.

YamlNodes can be written to a YAML stream with ToYaml(), ToYaml(Stream), ToYaml(TextWriter) and ToYamlFile(string).

The way of serialization can be configured in some aspects. The custom settings are specified by an instance of YamlConfig class. The serialization methods introduced above has overloaded styles that accepts YamlConfig instance to customize serialization. It is also possible to customize the default serialization method by modifying YamlNode.DefaultConfig static property.

YamlScalar has Value property, which holds the string expression of the node value.

YamlSequence implements IList<YamlNode> interface to access the child nodes.

YamlMapping implements IDictionary<YamlNode,YamlNode> interface to access the key / value pairs under the node.

Implicit conversion from C# native object to YamlScalar

Implicit cast operators from String, Boolean, Int32, Double and DateTime to YamlNode is defined.

Thus, anytime YamlNode is required in C# source, naked scalar value can be written. Namely, methods of YamlSequence and YamlMapping accept such C# native types as arguments in addition to YamlNode types.

var map = new YamlMapping();
map["Time"] = DateTime.Now;                 // implicitly converted to YamlScalar
Assert.IsTrue(map.ContainsKey(new YamlScalar("Time")));
Assert.IsTrue(map.ContainsKey("Time"));     // implicitly converted to YamlScalar

Extraction of C# native object from YamlScalar

If YamlScalar has a YAML's standard tag, such as !!int and !!bool, and a valid value, NativeObjectAvailable property is set true.
In such a case, the native object can be read from its NativeObject property.

Equality of YamlNodes

Equality of YamlNodes are evaluated on the content base as far as possible. Different YamlNode objects that have the same content are evaluated to be equal. Use Equals(object) method for equality evaluation.

Since the library does not know how to evaluate the equality of YamlNodes that have some non standard tag, equality of such nodes is evaluated only by nodes' identity.

When two YamlNodes have a YAML's standard tag, they are logically equal to each other when the YamlNode and its child nodes have the same contents (Tag and Value) and their node graph topology is exactly same.

YamlNode a1 = "a";  // implicit conversion
YamlNode a2 = "a";  // implicit conversion
YamlNode a3 = new YamlNode("!char", "a");
YamlNode b  = "b";  // implicit conversion

Assert.IsTrue(a1 != a2);        // different objects
Assert.IsTrue(a1.Equals(a2));   // different objects having same content

Assert.IsFalse(a1.Equals(a3));  // Tag is different
Assert.IsFalse(a1.Equals(b));   // Value is different

var s1 = new YamlMapping(a1, new YamlSequence(a1, a2));
var s2 = new YamlMapping(a1, new YamlSequence(a2, a1));
var s3 = new YamlMapping(a2, new YamlSequence(a1, a2));

Assert.IsFalse(s1.Equals(s2)); // node graph topology is different
Assert.IsFalse(s1.Equals(s3)); // node graph topology is different
Assert.IsTrue(s2.Equals(s3));  // different objects having same content and node graph topology

When a YamlScalar has a Yaml's standard tag, such as "!!int" and "!!float", the equality of the nodes are evaluated on the native object base, independent on its string expression in the YAML document.

var seq = (YamlSequence) YamlNode.FromYaml(
  "- !!float 10\n" +
  "- 10.0\n" +
  "- 1E1\n" +
  "- +.1E2\n"
)[0];

Assert.AreEqual(seq[0], seq[1]);  // "!!float 10" == "10.0"
Assert.AreEqual(seq[0], seq[2]);  // "!!float 10" == "1E1"
Assert.AreEqual(seq[0], seq[3]);  // "!!float 10" == "10.0"
Assert.AreEqual(seq[0], seq[3]);  // "!!float 10" == "+.1E2"

Full length example

This is from the Example 2.27 in YAML 1.2 specification.

// %YAML 1.2
// --- 
// !<tag:clarkevans.com,2002:invoice>
// invoice: 34843
// date   : 2001-01-23
// bill-to: &id001
//     given  : Chris
//     family : Dumars
//     address:
//         lines: |
//             458 Walkman Dr.
//             Suite #292
//         city    : Royal Oak
//         state   : MI
//         postal  : 48046
// ship-to: *id001
// product:
//     - sku         : BL394D
//       quantity    : 4
//       description : Basketball
//       price       : 450.00
//     - sku         : BL4438H
//       quantity    : 1
//       description : Super Hoop
//       price       : 2392.00
// tax  : 251.42
// total: 4443.52
// comments:
//     Late afternoon is best.
//     Backup contact is Nancy
//     Billsmer @ 338-4338.
// ...

var invoice = new YamlMapping(
    "invoice", 34843,
    "date", new DateTime(2001, 01, 23),
    "bill-to", new YamlMapping(
        "given", "Chris",
        "family", "Dumars",
        "address", new YamlMapping(
            "lines", "458 Walkman Dr.\nSuite #292\n",
            "city", "Royal Oak",
            "state", "MI",
            "postal", 48046
            )
        ),
    "product", new YamlSequence(
        new YamlMapping(
            "sku",         "BL394D",
            "quantity",    4,
            "description", "Basketball",
            "price",       450.00
            ),
        new YamlMapping(
            "sku",         "BL4438H",
            "quantity",    1,
            "description", "Super Hoop",
            "price",       2392.00
            )
        ),
    "tax", 251.42,
    "total", 4443.52,
    "comments", "Late afternoon is best. Backup contact is Nancy Billsmer @ 338-4338."
    );
invoice["ship-to"] = invoice["bill-to"];
invoice.Tag = "tag:clarkevans.com,2002:invoice";

invoice.ToYamlFile("invoice.yaml");
// %YAML 1.2
// ---
// !<tag:clarkevans.com,2002:invoice>
// invoice: 34843
// date: 2001-01-23
// bill-to: &A 
//   given: Chris
//   family: Dumars
//   address: 
//     lines: "458 Walkman Dr.\n\
//       Suite #292\n"
//     city: Royal Oak
//     state: MI
//     postal: 48046
// product: 
//   - sku: BL394D
//     quantity: 4
//     description: Basketball
//     price: !!float 450
//   - sku: BL4438H
//     quantity: 1
//     description: Super Hoop
//     price: !!float 2392
// tax: 251.42
// total: 4443.52
// comments: Late afternoon is best. Backup contact is Nancy Billsmer @ 338-4338.
// ship-to: *A
// ...

Last edited Oct 4, 2009 at 10:03 AM by osamu, version 7

Comments

No comments yet.