Abstract base class of YAML data nodes.

See YamlScalar, YamlSequence and YamlMapping for actual data classes.

YAML data model

See http://yaml.org/ for the official definition of 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 class that represents a YAML node.

In reality, a YamlNode is either YamlScalar, YamlSequence or YamlMapping.

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

Default Tag value for YamlScalar, YamlSequence or YamlMapping are "tag:yaml.org,2002:str", "tag:yaml.org,2002:seq", "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 be also represented in a short 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 YamlNode.ExpandTag and YamlNode.ShorthandTag(string). In addition to these three basic tags, this library uses "!!null", "!!bool", "!!int", "!!float" and "!!timestamp" tags, by default.

YamlNodes can be read from a YAML stream with YamlNode.FromYaml(string), YamlNode.FromYaml(Stream), YamlNode.FromYaml(TextReader) and YamlNode.FromYamlFile(string) static methods. Since a YAML stream generally consist of multiple YAML documents, each of which has a 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 YamlNode.ToYaml(), YamlNode.ToYaml(Stream), YamlNode.ToYaml(TextWriter) and YamlNode.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 change the default serialization method by modifying YamlNode.DefaultConfig static property.

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

A 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, bool, int, 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

Equality of YamlNodes

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

In detail, two YamlNodes are logically equal to each other when the YamlNode and its child nodes have the same contents (YamlNode.Tag and YamlScalar.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
Inheritance: IRehashableKey
 public void Add(string anchor_name, YamlNode node)
 {
     if ( Items.ContainsKey(anchor_name) ) {
         // override an existing anchor
         ItemsToRewind.Push(new RewindInfo(anchor_name, this[anchor_name]));
         Items[anchor_name] = node;
     } else {
         ItemsToRewind.Push(new RewindInfo(anchor_name, null));
         Items.Add(anchor_name, node);
     }
 }
 public RewindInfo(string anchor_name, YamlNode old_value)
 {
     this.anchor_name = anchor_name;
     this.old_value = old_value;
 }
 public static void outputNode(YamlNode node,string ind="")
 {
     Console.WriteLine(ind+"NODE:\n{0}", ind+node.Tag);
     if (node.GetType() == typeof(YamlMapping))
         foreach (YamlNode n in ((YamlMapping)node).Keys)
         {
             outputNode(n,ind+"-");
         }
     else if (node.GetType() == typeof(YamlScalar))
         Console.WriteLine("{0}\n", ind+((YamlScalar)node));
 }
 internal override bool Equals(YamlNode b, ObjectRepository repository)
 {
     bool skip;
     if(! base.EqualsSub(b, repository, out skip) )
         return false;
     if(skip)
         return true;
     YamlScalar aa = this;
     YamlScalar bb = (YamlScalar)b;
     if ( NativeObjectAvailable ) {
         return bb.NativeObjectAvailable &&
                (aa.NativeObject == null ?
                                             bb.NativeObject==null :
                                                                       aa.NativeObject.Equals(bb.NativeObject) );
     } else {
         if ( ShorthandTag() == "!!str" ) {
             return aa.Value == bb.Value;
         } else {
             // Node with non standard tag is compared by its identity.
             return false;
         }
     }
 }
 public bool AlreadyAppeared(YamlNode a, YamlNode b, out bool identity)
 {
     int ai, bi;
     bool ar = nodes_a.TryGetValue(a, out ai);
     bool br = nodes_b.TryGetValue(b, out bi);
     if ( ar && br && ai == bi ) {
         identity = true;
         return true;
     }
     if ( ar ^ br ) {
         identity = false;
         return true;
     }
     nodes_a.Add(a, nodes_a.Count);
     nodes_b.Add(b, nodes_b.Count);
     stack_a.Push(a);
     stack_b.Push(b);
     if ( a == b ) {
         identity = true;
         return true;
     }
     identity = false;
     return false;
 }
 /// <summary>
 /// Returns true if <paramref name="b"/> is of same type as the <see cref="YamlNode"/> and
 /// its Tag is same as the node. It returns true for <paramref name="skip"/> if they
 /// both already appeared in the node trees and were compared.
 /// </summary>
 /// <param name="b">Node to be compared.</param>
 /// <param name="repository">Node repository holds the nodes that already appeared and 
 /// the corresponding node in the other node tree.</param>
 /// <param name="skip">true if they already appeared in the node tree and were compared.</param>
 /// <returns>true if they are equal to each other.</returns>
 internal bool EqualsSub(YamlNode b, ObjectRepository repository, out bool skip)
 {
     YamlNode a = this;
     bool identity;
     if ( repository.AlreadyAppeared(a, b, out identity) ) {
         skip = true;
         return identity;
     }
     skip = false;
     if ( a.GetType() != b.GetType() || a.Tag != b.Tag )
         return false;
     return true;
 }
 /// <summary>
 /// Returns true if <paramref name="b"/> is of same type as the <see cref="YamlNode"/> and
 /// its content is also logically same.
 /// </summary>
 /// <param name="b">Node to be compared.</param>
 /// <param name="repository">Node repository holds the nodes that already appeared and 
 /// the corresponding node in the other node tree.</param>
 /// <returns>true if they are equal to each other.</returns>
 internal abstract bool Equals(YamlNode b, ObjectRepository repository);