API Overview
Introduction
NimYAML advocates parsing YAML input into native Nim types. Basic Nim library types like integers, floats and strings, as well as all tuples, enums and objects without private fields are supported out-of-the-box. Reference types are also supported, and NimYAML is able to detect if a reference occurs more than once and will serialize it accordingly. This means that NimYAML is able to dump and load potentially cyclic objects.
While loading into and dumping from native Nim types is the preferred way to use NimYAML, it also gives you complete control over each processing step, so that you can for example only use the parser and process its event stream yourself. The following diagram gives an overview of NimYAML's features based on the YAML processing pipeline. The items and terminology YAML defines is shown in italic, NimYAML's implementation name is shown in bold.
Intermediate Representation
The base of all YAML processing with NimYAML is the stream: YamlStream. This is an iterator over data: Event objects. Every proc that represents a single stage of the loading or dumping process will either take a YamlStream as input or return a YamlStream. Procs that implement the whole process in one step hide the YamlStream from the caller. Every proc that returns a YamlStream guarantees that this stream is well-formed according to the YAML specification.
This stream-oriented API can efficiently be used to parse large amounts of data. The drawback is that errors in the input are only discovered while processing the YamlStream. If the YamlStream encounters an exception while producing the next event, it will throw a stream: YamlStreamError which contains the original exception as parent. The caller should know which exceptions are possible as parents of YamlStream because they know the source of the YamlStream they provided.
Loading YAML
If you want to load YAML character data directly into a native Nim variable, you can use loading: load. This is the easiest and recommended way to load YAML data. This section gives an overview about how load is implemented. It is absolutely possible to reimplement the loading step using the low-level API.
For parsing, a parser: YamlParser object is needed. This object stores some state while parsing that may be useful for error reporting to the user. The parser: parse proc implements the YAML processing step of the same name. All syntax errors in the input character stream are processed by parse, which will raise a parser: YamlParserError if it encounters a syntax error.
Transforming a YamlStream to a native YAML object is done via native: construct. It skips the compose step for efficiency reasons. As Nim is statically typed, you have to know the target type when you write your loading code. This is different from YAML APIs of dynamically typed languages. If you cannot know the type of your YAML input at compile time, you have to manually process the YamlStream to serve your needs.
Dumping YAML
Dumping is preferably done with dumping: dump, which serializes a native Nim value to a character stream. As with load, this section describes how dump is implemented using the low-level API.
A Nim value is transformed into a YamlStream with native: represent. Depending on the native: AnchorStyle you specify in the native: SerializationOptions of your dumping: Dumper, this will transform ref variables with multiple instances into anchored elements and aliases (for asTidy and asAlways) or write the same element into all places it occurs (for asNone). Be aware that if you use asNone, the value you serialize might not round-trip.
Transforming a YamlStream into YAML character data is done with presenter: present which is customized by your Dumper's presenter: PresentationOptions. The Dumper provides multiple presets, for example the dumping: jsonDumper dumps your value in JSON style (which is also valid YAML since YAML is a superset of JSON).
The Document Object Model
Unlike XML, YAML does not define an official document object model. However, if you cannot or do not want to load a YAML input stream to native Nim types, you can load it into the predefined type dom: YamlNode. You can also use this type inside your native types to deserialize parts of the YAML input into it. Likewise, you can serialize a YamlNode into YAML. You can use this to preserve parts of YAML data you do not wish to or cannot fully deserialize.
A YamlNode preserves its given tag and the tags of any child nodes, and also its style (which means, unless you override style with Dumper options, the node will be serialized with the same style it had originally). However, anchors will be resolved during loading and re-added during serialization. It is possible for a YamlNode to occur multiple times within source/target root object, in which case it will be serialized once and referred to afterwards via aliases.
YamlNode is allocated on the heap and using it will be slower and consume more memory than deserializing into native types.