Major new features are:
A complete list of changes is available here. NimYAML 0.7.0 requires Nim 0.15.0.
Moreover, timestamps can now be parsed into Time values. However, this is only usable with most recent Nim devel version, since timezones are broken in 0.15.2.
Hi @flyx!
I'm using NimYAML to parse a config file. I want to use custom tags but can't figure out form the docs how to do it.
Here's a glimpse of what I want to be able to handle:
chapters:
- foo
- bar
- !remote http://example.com/somefile
When I load this content with loadToJson and iterate over [0]["chapters"] the tag is ignored. I tried registering the tag URI but I don't know what to with with the TagId I get from it.
Could you please help me with that?
loadToJson ignores tags because the JSON structures do not know anything about tags. You have several options:
Using the serialization API with an implicit variant object
First, you need to define the type for the tag !remote. You need to tell NimYAML how to load that type. Then, you define a variant object type that can hold either normal strings or !remote values. You then tell NimYAML to treat this variant object type implicitly for loading the chapters. Example code:
import yaml.serialization, yaml.taglib
type
Remote = distinct string
ChapterKind = enum
ckLocal, ckRemote
Chapter = object
case kind: ChapterKind
of ckLocal:
lValue: string
of ckRemote:
rValue: Remote
Root = object
chapters: seq[Chapter]
proc constructObject*(s: var YamlStream, c: ConstructionContext,
result: var Remote) =
# construct a Remote like a string
constructObject(s, c, string(result))
setTagUri(Remote, "!remote")
markAsImplicit(Chapter)
var root: Root
load("""
chapters:
- foo
- bar
- !remote http://example.com/somefile
""", root)
echo "Parsed chapters:"
for c in root.chapters:
case c.kind
of ckLocal: echo "[Local] ", c.lValue
of ckRemote: echo "[Remote] ", string(c.rValue)
Sadly, you cannot simply transform the chapter values into some tuple[value: String, remote: bool] because NimYAML's serialization API uses tags to denote types, not annotations.
Using the DOM API
Perhaps simpler for simple use cases:
import yaml.dom
var document = loadDOM("""
chapters:
- foo
- bar
- !remote http://example.com/somefile
""")
echo "Parsed chapters:"
for c in document.root.pairs[0].value.children:
echo if c.tag == "!remote": "[Remote] " else: "[Local] ", c.content
The DOM API needs some love; currently mappings are parsed into a seq[tuple[key, value: YamlNode]] which is not ideal for accessing specific subtrees. A Table would make more sense. Pull requests are welcome :).
Using the sequential API
This may be useful for use cases where you need to process the input sequentially since it doesn't load everything in an object and therefore is faster:
import yaml.parser, yaml.taglib, yaml.stream
var
myTagLib = initCoreTagLibrary()
yTagRemote = myTagLib.registerUri("!remote")
p = newYamlParser(myTagLib)
events = p.parse("""
chapters:
- foo
- bar
- !remote http://example.com/somefile
""")
doAssert events.next().kind == yamlStartDoc
doAssert events.next().kind == yamlStartMap
let key = events.next()
doAssert key.kind == yamlScalar
doAssert key.scalarContent == "chapters"
doAssert events.next().kind == yamlStartSeq
var cur = events.next()
echo "Parsed chapters:"
while cur.kind != yamlEndSeq:
doAssert cur.kind == yamlScalar
echo if cur.scalarTag == yTagRemote: "[Remote] " else: "[Local] ", cur.scalarContent
cur = events.next()
doAssert events.next().kind == yamlEndMap
doAssert events.next().kind == yamlEndDoc
doAssert events.finished()