NimYAML
Introduction
NimYAML is a pure YAML implementation for Nim. It is able to read from and write to YAML character streams, and to serialize from and construct to native Nim types. It exclusively supports YAML 1.2.
Source code can be found on GitHub. You can install it with Nimble:
nimble install yaml
You can find a conceptual overview of the library here and an overview over the library's API here.
NimYAML 2.x
NimYAML 2.0.0 introduced some breaking changes. Read about migrating to the new version here.
Quickstart
Dumping Nim objects as YAML
code.nim | out.yaml |
---|---|
import yaml, streams type Person = object name : string age : int32 var personList = newSeq[Person]() personList.add(Person(name: "Karl Koch", age: 23)) personList.add(Person(name: "Peter Pan", age: 12)) var s = newFileStream("out.yaml", fmWrite) Dumper().dump(personList, s) s.close() |
- name: Karl Koch age: 23 - name: Peter Pan age: 12 |
Loading Nim objects from YAML
code.nim | in.yaml |
---|---|
import yaml, streams type Person = object name : string age : int32 var personList: seq[Person] var s = newFileStream("in.yaml") load(s, personList) s.close() |
- { name: Karl Koch, age: 23 } - { name: Peter Pan, age: 12 } |
Customizing output style
… via style pragmas
code.nim | out.yaml |
---|---|
import yaml, yaml/style, streams type Strings = object first {.scalar: ssSingleQuoted.}: string second {.scalar: ssLiteral.}: string third {.scalar: ssDoubleQuoted.}: string Numbers {.collection: csFlow.} = object start, stop: int32 Root = object strings: Strings numbers: Numbers blockNumbers {.collection: csBlock.}: Numbers var root = Root( strings: Strings( first: "foo", second: "bar\n", third: "baz" ), numbers: Numbers(start: 0, stop: 23), blockNumbers: Numbers(start: 23, stop: 42) ) var s = newFileStream("out.yaml", fmWrite) Dumper().dump(root, s) s.close() |
strings: first: 'foo' second: | bar third: "baz" numbers: { start: 0, stop: 23 } blockNumbers: start: 23 stop: 42 |
… by customizing the Dumper
code.nim | out.yaml |
---|---|
import yaml, streams type Person = object name: string age: int32 var personList = newSeq[Person]() personList.add(Person(name: "Karl Koch", age: 23)) personList.add(Person(name: "Peter Pan", age: 12)) var s = newFileStream("out.yaml", fmWrite) var dumper = explanatoryDumper() dumper.presentation.indentationStep = 3 dumper.presentation.newlines = nlLF dumper.presentation.outputVersion = ov1_1 dumper.dump(personList, s) s.close() |
%YAML 1.1 %TAG !n! tag:nimyaml.org,2016: --- !n!system:seq(tag:nimyaml.org;2016:custom:Person) [ !n!custom:Person { ? !n!field "name" : !!str "Karl Koch", ? !n!field "age" : !n!system:int32 "23" }, !n!custom:Person { ? !n!field "name" : !!str "Peter Pan", ? !n!field "age" : !n!system:int32 "12" } ] |
Dumping reference types and cyclic structures
code.nim | out.yaml |
---|---|
import yaml, streams type Node = ref NodeObj NodeObj = object name: string left, right: Node var node1, node2, node3: Node new(node1); new(node2); new(node3) node1.name = "Node 1" node2.name = "Node 2" node3.name = "Node 3" node1.left = node2 node1.right = node3 node2.right = node3 node3.left = node1 var s = newFileStream("out.yaml", fmWrite) Dumper().dump(node1, s) s.close() |
--- &a name: Node 1 left: name: Node 2 left: !!null ~ right: &b name: Node 3 left: *a right: !!null ~ right: *b |
Loading reference types and cyclic structures
code.nim | in.yaml |
---|---|
import yaml, streams type Node = ref NodeObj NodeObj = object name: string left, right: Node var node1: Node var s = newFileStream("in.yaml") load(s, node1) s.close() |
--- &a name: Node 1 left: name: Node 2 left: ~ right: &b name: Node 3 left: *a right: ~ right: *b |
Defining a custom tag uri for a type
code.nim | out.yaml |
---|---|
import yaml, streams type Mob = object level, experience: int32 drops: seq[string] setTag(Mob, Tag("!Mob")) setTag(seq[string], Tag("!Drops")) var mob = Mob(level: 42, experience: 1800, drops: @["Sword of Mob Slaying"]) var s = newFileStream("out.yaml", fmWrite) var dumper = Dumper() dumper.serialization.tagStyle = tsAll dumper.serialization.handles = initNimYamlTagHandle() dumper.dump(mob, s) s.close() |
%TAG !n! tag:nimyaml.org,2016: --- !Mob !n!field level: !n!system:int32 42 !n!field experience: !n!system:int32 1800 !n!field drops: !Drops [!!str Sword of Mob Slaying] |
Dumping Nim objects as JSON
code.nim | out.yaml |
---|---|
import yaml, streams type Person = object name : string age : int32 var personList = newSeq[Person]() personList.add(Person(name: "Karl Koch", age: 23)) personList.add(Person(name: "Peter Pan", age: 12)) var s = newFileStream("out.yaml", fmWrite) jsonDumper().dump(personList, s) s.close() |
[ { "name": "Karl Koch", "age": 23 }, { "name": "Peter Pan", "age": 12 } ] |
Loading Nim objects from JSON
code.nim | in.yaml |
---|---|
import yaml, streams type Person = object name : string age : int32 var personList: seq[Person] var s = newFileStream("in.yaml") load(s, personList) s.close() |
[ { "name": "Karl Koch", "age": 23 }, { "name": "Peter Pan", "age": 12 } ] |
Processing a Sequence of Heterogeneous Items
… with variant objects
code.nim | in.yaml |
---|---|
import yaml, streams type Person = object name: string ContainerKind = enum ckString, ckInt, ckBool, ckPerson, ckNone # {.implicit.} tells NimYAML to use Container # as implicit type. # only possible with variant object types where # each branch contains at most one object. Container {.implicit.} = object case kind: ContainerKind of ckString: strVal: string of ckInt: intVal: int of ckBool: boolVal: bool of ckPerson: personVal: Person of ckNone: discard setTag(Person, Tag("!Person")) var list: seq[Container] var s = newFileStream("in.yaml") load(s, list) s.close() assert(list[0].kind == ckString) assert(list[0].strVal == "this is a string") # and so on |
- this is a string - 42 - false - !!str 23 - !Person {name: Trillian} - !!null |
… with the YamlStream API
code.nim | in.yaml |
---|---|
import yaml, yaml/data, yaml/parser, yaml/hints, streams type Person = object name: string setTag(Person, Tag("!Person"), yTagPerson) var s = newFileStream("in.yaml", fmRead) yamlParser = initYamlParser() events = yamlParser.parse(s) context = initConstructionContext(events) assert events.next().kind == yamlStartStream assert events.next().kind == yamlStartDoc assert events.next().kind == yamlStartSeq var nextEvent = events.peek() while nextEvent.kind != yamlEndSeq: var curTag = nextEvent.properties().tag if curTag == yTagQuestionMark: # we only support implicitly tagged scalars assert nextEvent.kind == yamlScalar case guessType(nextEvent.scalarContent) of yTypeInteger: curTag = yTagInteger of yTypeBoolTrue, yTypeBoolFalse: curTag = yTagBoolean of yTypeUnknown: curTag = yTagString else: assert false, "Type not supported!" elif curTag == yTagExclamationMark: curTag = yTagString case curTag of yTagString: var s: string context.constructChild(s) echo "got string: ", s of yTagInteger: var i: int32 context.constructChild(i) echo "got integer: ", i of yTagBoolean: var b: bool context.constructChild(b) echo "got boolean: ", b of yTagPerson: var p: Person context.constructChild(p) echo "got Person with name: ", p.name else: assert false, "unsupported tag: " & $curTag nextEvent = events.peek() assert events.next().kind == yamlEndSeq assert events.next().kind == yamlEndDoc assert events.next().kind == yamlEndStream s.close() |
- this is a string - 42 - false - !!str 23 - !Person {name: Trillian} |