Support nil strings and seqs

This commit is contained in:
Felix Krause 2016-04-04 21:21:24 +02:00
parent 58b9b92895
commit 4ca4b2c87e
5 changed files with 108 additions and 15 deletions

View File

@ -62,7 +62,9 @@ supported. There is currently no problem with ``float``, because it is always a
``float64``. ``float64``.
``string`` is supported and one of the few Nim types which directly map to a ``string`` is supported and one of the few Nim types which directly map to a
standard YAML type. ``char`` is also supported. standard YAML type. NimYAML is able to handle strings that are ``nil``, they
will be serialized with the special tag ``!nim:nil:string``. ``char`` is also
supported.
To support new scalar types, you must implement the ``constructObject()`` and To support new scalar types, you must implement the ``constructObject()`` and
``representObject()`` procs on that type (see below). ``representObject()`` procs on that type (see below).
@ -86,6 +88,9 @@ cannot be loaded to Nim collection types. For example, this sequence:
Cannot be loaded to a Nim ``seq``. For this reason, you cannot load YAML's Cannot be loaded to a Nim ``seq``. For this reason, you cannot load YAML's
native ``!!map`` and ``!!seq`` types directly into Nim types. native ``!!map`` and ``!!seq`` types directly into Nim types.
Nim ``seq`` types may be ``nil``. This is handled by serializing them to an
empty scalar with the tag ``!nim:nil:seq``.
Reference Types Reference Types
--------------- ---------------

View File

@ -488,6 +488,37 @@ proc constructChild*[T](s: var YamlStream, c: ConstructionContext,
else: assert false else: assert false
constructObject(s, c, result) constructObject(s, c, result)
proc constructChild*(s: var YamlStream, c: ConstructionContext,
result: var string) =
let item = s.peek()
if item.kind == yamlScalar:
if item.scalarTag == yTagNimNilString:
discard s.next()
result = nil
return
elif item.scalarTag notin
[yTagQuestionMark, yTagExclamationMark, yamlTag(string)]:
raise newException(YamlConstructionError, "Wrong tag for string")
elif item.scalarAnchor != yAnchorNone:
raise newException(YamlConstructionError, "Anchor on non-ref type")
constructObject(s, c, result)
proc constructChild*[T](s: var YamlStream, c: ConstructionContext,
result: var seq[T]) =
let item = s.peek()
if item.kind == yamlScalar:
if item.scalarTag == yTagNimNilSeq:
discard s.next()
result = nil
return
elif item.kind == yamlStartSeq:
if item.seqTag notin [yTagQuestionMark, yamlTag(seq[T])]:
raise newException(YamlConstructionError, "Wrong tag for " &
typetraits.name(seq[T]))
elif item.seqAnchor != yAnchorNone:
raise newException(YamlConstructionError, "Anchor on non-ref type")
constructObject(s, c, result)
proc constructChild*[O](s: var YamlStream, c: ConstructionContext, proc constructChild*[O](s: var YamlStream, c: ConstructionContext,
result: var ref O) = result: var ref O) =
var e = s.peek() var e = s.peek()
@ -527,6 +558,20 @@ proc representChild*[O](value: O, ts: TagStyle, c: SerializationContext):
RawYamlStream = RawYamlStream =
result = representObject(value, ts, c, presentTag(O, ts)) result = representObject(value, ts, c, presentTag(O, ts))
proc representChild*(value: string, ts: TagStyle, c: SerializationContext):
RawYamlStream =
if isNil(value):
result = iterator(): YamlStreamEvent =
yield scalarEvent("", yTagNimNilString)
else: result = representObject(value, ts, c, presentTag(string, ts))
proc representChild*[T](value: seq[T], ts: TagStyle, c: SerializationContext):
RawYamlStream =
if isNil(value):
result = iterator(): YamlStreamEvent =
yield scalarEvent("", yTagNimNilSeq)
else: result = representObject(value, ts, c, presentTag(seq[T], ts))
proc representChild*[O](value: ref O, ts: TagStyle, c: SerializationContext): proc representChild*[O](value: ref O, ts: TagStyle, c: SerializationContext):
RawYamlStream = RawYamlStream =
if value == nil: if value == nil:

View File

@ -79,3 +79,5 @@ proc initSerializationTagLibrary(): TagLibrary =
result.tags["tag:yaml.org,2002:value"] = yTagValue result.tags["tag:yaml.org,2002:value"] = yTagValue
result.tags["tag:yaml.org,2002:binary"] = yTagBinary result.tags["tag:yaml.org,2002:binary"] = yTagBinary
result.tags["!nim:field"] = yTagNimField result.tags["!nim:field"] = yTagNimField
result.tags["!nim:nil:string"] = yTagNimNilString
result.tags["!nim:nil:seq"] = yTagNimNilSeq

View File

@ -93,6 +93,18 @@ suite "Serialization":
except: gotException = true except: gotException = true
assert gotException, "Expected exception, got none." assert gotException, "Expected exception, got none."
test "Serialization: Load nil string":
let input = newStringStream("!nim:nil:string \"\"")
var result: string
load(input, result)
assert isNil(result)
test "Serialization: Dump nil string":
let input: string = nil
var output = newStringStream()
dump(input, output, tsNone, asTidy, blockOnly)
assertStringEqual "%YAML 1.2\n--- \n!nim:nil:string \"\"", output.data
test "Serialization: Load string sequence": test "Serialization: Load string sequence":
let input = newStringStream(" - a\n - b") let input = newStringStream(" - a\n - b")
var result: seq[string] var result: seq[string]
@ -101,12 +113,24 @@ suite "Serialization":
assert result[0] == "a" assert result[0] == "a"
assert result[1] == "b" assert result[1] == "b"
test "Serialization: Represent string sequence": test "Serialization: Dump string sequence":
var input = @["a", "b"] var input = @["a", "b"]
var output = newStringStream() var output = newStringStream()
dump(input, output, tsNone, asTidy, blockOnly) dump(input, output, tsNone, asTidy, blockOnly)
assertStringEqual "%YAML 1.2\n--- \n- a\n- b", output.data assertStringEqual "%YAML 1.2\n--- \n- a\n- b", output.data
test "Serialization: Load nil seq":
let input = newStringStream("!nim:nil:seq \"\"")
var result: seq[int]
load(input, result)
assert isNil(result)
test "Serialization: Dump nil seq":
let input: seq[int] = nil
var output = newStringStream()
dump(input, output, tsNone, asTidy, blockOnly)
assertStringEqual "%YAML 1.2\n--- \n!nim:nil:seq \"\"", output.data
test "Serialization: Load char set": test "Serialization: Load char set":
let input = newStringStream("- a\n- b") let input = newStringStream("- a\n- b")
var result: set[char] var result: set[char]
@ -115,7 +139,7 @@ suite "Serialization":
assert 'a' in result assert 'a' in result
assert 'b' in result assert 'b' in result
test "Serialization: Represent char set": test "Serialization: Dump char set":
var input = {'a', 'b'} var input = {'a', 'b'}
var output = newStringStream() var output = newStringStream()
dump(input, output, tsNone, asTidy, blockOnly) dump(input, output, tsNone, asTidy, blockOnly)
@ -129,7 +153,7 @@ suite "Serialization":
assert result[1] == 42 assert result[1] == 42
assert result[2] == 47 assert result[2] == 47
test "Serialization: Represent array": test "Serialization: Dump array":
let input = [23'i32, 42'i32, 47'i32] let input = [23'i32, 42'i32, 47'i32]
var output = newStringStream() var output = newStringStream()
dump(input, output, tsNone, asTidy, blockOnly) dump(input, output, tsNone, asTidy, blockOnly)
@ -143,7 +167,7 @@ suite "Serialization":
assert result[23] == "dreiundzwanzig" assert result[23] == "dreiundzwanzig"
assert result[42] == "zweiundvierzig" assert result[42] == "zweiundvierzig"
test "Serialization: Represent Table[int, string]": test "Serialization: Dump Table[int, string]":
var input = initTable[int32, string]() var input = initTable[int32, string]()
input[23] = "dreiundzwanzig" input[23] = "dreiundzwanzig"
input[42] = "zweiundvierzig" input[42] = "zweiundvierzig"
@ -168,7 +192,7 @@ suite "Serialization":
else: assert false else: assert false
i.inc() i.inc()
test "Serialization: Represent OrderedTable[tuple[int32, int32], string]": test "Serialization: Dump OrderedTable[tuple[int32, int32], string]":
var input = initOrderedTable[tuple[a, b: int32], string]() var input = initOrderedTable[tuple[a, b: int32], string]()
input.add((a: 23'i32, b: 42'i32), "dreiundzwanzigzweiundvierzig") input.add((a: 23'i32, b: 42'i32), "dreiundzwanzigzweiundvierzig")
input.add((a: 13'i32, b: 47'i32), "dreizehnsiebenundvierzig") input.add((a: 13'i32, b: 47'i32), "dreizehnsiebenundvierzig")
@ -196,7 +220,7 @@ suite "Serialization":
assert result[1] == @[4.int32, 5.int32] assert result[1] == @[4.int32, 5.int32]
assert result[2] == @[6.int32] assert result[2] == @[6.int32]
test "Serialization: Represent Sequences in Sequence": test "Serialization: Dump Sequences in Sequence":
let input = @[@[1.int32, 2.int32, 3.int32], @[4.int32, 5.int32], @[6.int32]] let input = @[@[1.int32, 2.int32, 3.int32], @[4.int32, 5.int32], @[6.int32]]
var output = newStringStream() var output = newStringStream()
dump(input, output, tsNone) dump(input, output, tsNone)
@ -212,7 +236,7 @@ suite "Serialization":
assert result[1] == tlGreen assert result[1] == tlGreen
assert result[2] == tlYellow assert result[2] == tlYellow
test "Serialization: Represent Enum": test "Serialization: Dump Enum":
let input = @[tlRed, tlGreen, tlYellow] let input = @[tlRed, tlGreen, tlYellow]
var output = newStringStream() var output = newStringStream()
dump(input, output, tsNone, asTidy, blockOnly) dump(input, output, tsNone, asTidy, blockOnly)
@ -227,7 +251,7 @@ suite "Serialization":
assert result.i == 42 assert result.i == 42
assert result.b == true assert result.b == true
test "Serialization: Represent Tuple": test "Serialization: Dump Tuple":
let input = (str: "value", i: 42.int32, b: true) let input = (str: "value", i: 42.int32, b: true)
var output = newStringStream() var output = newStringStream()
dump(input, output, tsNone) dump(input, output, tsNone)
@ -241,7 +265,7 @@ suite "Serialization":
assert result.surname == "Pan" assert result.surname == "Pan"
assert result.age == 12 assert result.age == 12
test "Serialization: Represent custom object": test "Serialization: Dump custom object":
let input = Person(firstnamechar: 'P', surname: "Pan", age: 12) let input = Person(firstnamechar: 'P', surname: "Pan", age: 12)
var output = newStringStream() var output = newStringStream()
dump(input, output, tsNone, asTidy, blockOnly) dump(input, output, tsNone, asTidy, blockOnly)
@ -256,7 +280,7 @@ suite "Serialization":
assert result[0] == "one" assert result[0] == "one"
assert result[1] == "two" assert result[1] == "two"
test "Serialization: Represent sequence with explicit tags": test "Serialization: Dump sequence with explicit tags":
let input = @["one", "two"] let input = @["one", "two"]
var output = newStringStream() var output = newStringStream()
dump(input, output, tsAll, asTidy, blockOnly) dump(input, output, tsAll, asTidy, blockOnly)
@ -272,7 +296,7 @@ suite "Serialization":
assert result.surname == "Pan" assert result.surname == "Pan"
assert result.age == 12 assert result.age == 12
test "Serialization: Represent custom object with explicit root tag": test "Serialization: Dump custom object with explicit root tag":
let input = Person(firstnamechar: 'P', surname: "Pan", age: 12) let input = Person(firstnamechar: 'P', surname: "Pan", age: 12)
var output = newStringStream() var output = newStringStream()
dump(input, output, tsRootOnly, asTidy, blockOnly) dump(input, output, tsRootOnly, asTidy, blockOnly)
@ -280,7 +304,7 @@ suite "Serialization":
"--- !nim:custom:Person \nfirstnamechar: P\nsurname: Pan\nage: 12", "--- !nim:custom:Person \nfirstnamechar: P\nsurname: Pan\nage: 12",
output.data) output.data)
test "Serialization: Represent cyclic data structure": test "Serialization: Dump cyclic data structure":
var var
a = newNode("a") a = newNode("a")
b = newNode("b") b = newNode("b")
@ -342,7 +366,7 @@ next:
assert(result[0] == nil) assert(result[0] == nil)
assert(result[1][] == "~") assert(result[1][] == "~")
test "Serialization: Represent nil values": test "Serialization: Dump nil values":
var input = newSeq[ref string]() var input = newSeq[ref string]()
input.add(nil) input.add(nil)
input.add(new string) input.add(new string)

View File

@ -370,6 +370,11 @@ const
## This tag is used in serialization for the name of a field of an ## This tag is used in serialization for the name of a field of an
## object. It may contain any string scalar that is a valid Nim symbol. ## object. It may contain any string scalar that is a valid Nim symbol.
yTagNimNilString* : TagId = 101.TagId ## for strings that are nil
yTagNimNilSeq* : TagId = 102.TagId ## \
## for seqs that are nil. This tag is used regardless of the seq's generic
## type parameter.
yFirstCustomTagId* : TagId = 1000.TagId ## \ yFirstCustomTagId* : TagId = 1000.TagId ## \
## The first ``TagId`` which should be assigned to an URI that does not ## The first ``TagId`` which should be assigned to an URI that does not
## exist in the ``YamlTagLibrary`` which is used for parsing. ## exist in the ``YamlTagLibrary`` which is used for parsing.
@ -571,6 +576,18 @@ proc constructChild*[T](s: var YamlStream, c: ConstructionContext,
## for constructing the value. The ``ConstructionContext`` is needed for ## for constructing the value. The ``ConstructionContext`` is needed for
## potential child objects which may be refs. ## potential child objects which may be refs.
proc constructChild*(s: var YamlStream, c: ConstructionContext,
result: var string)
{.raises: [YamlConstructionError, YamlStreamError].}
## Constructs a Nim value that is a string from a part of a YAML stream.
## This specialization takes care of possible nil strings.
proc constructChild*[T](s: var YamlStream, c: ConstructionContext,
result: var seq[T])
{.raises: [YamlConstructionError, YamlStreamError].}
## Constructs a Nim value that is a string from a part of a YAML stream.
## This specialization takes care of possible nil seqs.
proc constructChild*[O](s: var YamlStream, c: ConstructionContext, proc constructChild*[O](s: var YamlStream, c: ConstructionContext,
result: var ref O) result: var ref O)
{.raises: [YamlConstructionError, YamlStreamError].} {.raises: [YamlConstructionError, YamlStreamError].}