Fork me on GitHub
NimYAML Home Testing Ground Docs: Overview Serialization Modules

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

Quickstart

Dumping Nim objects as YAML

code.nim out.yaml
import yaml
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)
dump(personList, s)
s.close()
%YAML 1.2
--- !nim:system:seq(nim:custom:Person)
-
  name: Karl Koch
  age: 23
-
  name: Peter Pan
  age: 12

Loading Nim objects from YAML

code.nim in.yaml
import yaml
type Person = object
  name : string
  age  : int32

var personList: seq[Person]
var s = newFileStream("in.yaml")
load(s, personList)
s.close()
%YAML 1.2
---
- { name: Karl Koch, age: 23 }
- { name: Peter Pan, age: 12 }

Customizing output style

code.nim out.yaml
import yaml
type Person = object
  name: string
  age: int32

var personList: seq[Person]
personList.add(Person(name: "Karl Koch", age: 23))
personList.add(Person(name: "Peter Pan", age: 12))

var s = newFileStream("out.yaml")
dump(personList, s, options = defineOptions(
    style = psCanonical,
    indentationStep = 3,
    newlines = nlLF,
    outputVersion = ov1_1))
s.close()
%YAML 1.1
--- !nim:system:seq(nim:custom:Person)
[
   !nim:custom:Person {
      ? !!str "name"
      : !!str "Karl Koch",
      ? !!str "age"
      : !nim:system:int32 "23"
   },
   !nim:custom:Person {
      ? !!str "name"
      : !!str "Peter Pan",
      ? !!str "age"
      : !nim:system:int32 "12"
   }
]

Dumping reference types and cyclic structures

code.nim out.yaml
import yaml
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)
dump(node1, s)
s.close()
%YAML 1.2
--- !nim:custom:NodeObj &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
type
  Node = ref NodeObj
  NodeObj = object
    name: string
    left, right: Node

var node1: Node

var s = newFileStream("in.yaml")
load(s, node1)
s.close()
%YAML 1.2
--- !nim:custom:NodeObj &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
type Mob = object
  level, experience: int32
  drops: seq[string]

setTagUri(Mob, "!Mob")
setTagUri(seq[string], "!Drops")

var mob = Mob(level: 42, experience: 1800, drops:
    @["Sword of Mob Slaying"])
var s = newFileStream("out.yaml", fmWrite)
dump(mob, s,
     options = defineOptions(tagStyle = tsAll))
s.close()
YAML 1.2
--- !Mob
!nim:field level: !nim:system:int32 42
!nim:field experience: !nim:system:int32 1800
!nim:field drops: !Drops [!!str Sword of Mob Slaying]

Dumping Nim objects as JSON

code.nim out.yaml
import yaml
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)
dump(personList, s,
     options = defineOptions(style = psJson))
s.close()
[
  {
    "name": "Karl Koch",
    "age": 23
  },
  {
    "name": "Peter Pan",
    "age": 12
  }
]

Loading Nim objects from JSON

code.nim in.yaml
import yaml
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
type
  Person = object
    name: string
  
  ContainerKind = enum
    ckString, ckInt, ckBool, ckPerson, ckNone
  
  Container = object
    case kind: ContainerKind
    of ckString:
      strVal: string
    of ckInt:
      intVal: int
    of ckBool:
      boolVal: bool
    of ckPerson:
      personVal: Person
    of ckNone:
      discard

setTagUri(Person, "!nim:demo:Person")

# tell NimYAML to use Container as implicit type.
# only possible with variant object types where
# each branch contains at most one object.
markAsImplicit(Container)

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
%YAML 1.2
---
- this is a string
- 42
- false
- !!str 23
- !nim:demo:Person {name: Trillian}
- !!null

… With the Sequential API

code.nim in.yaml
import yaml
type Person = object
  name: string

setTagUri(Person, "!nim:demo:Person", yTagPerson)

var
  s = newFileStream("in.yaml", fmRead)
  context = newConstructionContext()
  parser = newYamlParser(serializationTagLibrary)
  events = parser.parse(s)

assert events.next().kind == yamlStartDoc
assert events.next().kind == yamlStartSeq
var nextEvent = events.peek()
while nextEvent.kind != yamlEndSeq:
  var curTag = nextEvent.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
    events.constructChild(context, s)
    echo "got string: ", s
  of yTagInteger:
    var i: int32
    events.constructChild(context, i)
    echo "got integer: ", i
  of yTagBoolean:
    var b: bool
    events.constructChild(context, b)
    echo "got boolean: ", b
  of yTagPerson:
    var p: Person
    events.constructChild(context, 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.finished()
s.close()
%YAML 1.2
--- !!seq
- this is a string
- 42
- false
- !!str 23
- !nim:demo:Person {name: Trillian}