Documented custom serialization.

* Also improved style of testing ground
This commit is contained in:
Felix Krause 2016-02-16 20:08:44 +01:00
parent 151d795f07
commit d62aac16bc
3 changed files with 144 additions and 53 deletions

View File

@ -158,9 +158,54 @@ constructObject
result: var MyObject)
{.raises: [YamlConstructionError, YamlStreamError.}
This proc should construct the type from a ``YamlStream``.
This proc should construct the type from a ``YamlStream``. Follow the following
guidelines when implementing a custom ``constructObject`` proc:
**TBD: More documentation here**
- For constructing a value from a YAML scalar, consider using the
``constructScalarItem`` template, which will automatically catch exceptions
and wrap them with a ``YamlConstructionError``, and also will assure that the
item you use for construction is a ``yamlScalar``. See below for an example.
- For constructing a value from a YAML sequence or map, you **must** use the
``constructChild`` proc for child values if you want to use their
``constructObject`` implementation. This will check their tag and anchor.
Always try to construct child values that way.
- For non-scalars, make sure that the last value you remove from the stream is
the object's ending event (``yamlEndMap`` or ``yamlEndSequence``)
- Use `peek <yaml.html#peek,YamlStream>`_ for inspecting the next event in
the ``YamlStream`` without removing it.
- Never write a ``constructObject`` proc for a ``ref`` type. ``ref`` types are
always handled by NimYAML itself. You can only customize the construction of
the underlying object.
The following example for constructing from a YAML scalar value is the actual
implementation of constructing ``int`` types:
.. code-block:: nim
proc constructObject*[T: int8|int16|int32|int64](
s: var YamlStream, c: ConstructionContext, result: var T)
{.raises: [YamlConstructionError, YamlStreamError].} =
var item: YamlStreamEvent
constructScalarItem(s, item, name(T)):
result = T(parseBiggestInt(item.scalarContent))
The following example for constructiong from a YAML non-scalar is the actual
implementation of constructing ``seq`` types:
.. code-block:: nim
proc constructObject*[T](s: var YamlStream, c: ConstructionContext,
result: var seq[T])
{.raises: [YamlConstructionError, YamlStreamError].} =
let event = s.next()
if event.kind != yamlStartSequence:
raise newException(YamlConstructionError, "Expected sequence start")
result = newSeq[T]()
while s.peek().kind != yamlEndSequence:
var item: T
constructChild(s, c, item)
result.add(item)
discard s.next()
representObject
---------------
@ -171,6 +216,47 @@ representObject
c: SerializationContext): RawYamlStream {.raises: [].}
This proc should return an iterator over ``YamlStreamEvent`` which represents
the type.
the type. Follow the following guidelines when implementing a custom
``representObject`` proc:
**TBD: More documentation here**
- You can use the helper template
`presentTag <yaml.html#presentTag,typedesc,TagStyle>`_ for outputting the
tag.
- Always output the first tag with a ``yAnchorNone``. Anchors will be set
automatically by ``ref`` type handling.
- When outputting non-scalar types, you should use the ``representObject``
implementation of the child types, if possible.
- Check if the given ``TagStyle`` equals ``tsRootOnly`` and if yes, change it
to ``tsNone`` for the child values.
- Never write a ``representObject`` proc for ``ref`` types.
The following example for representing to a YAML scalar is the actual
implementation of representing ``int`` types:
.. code-block:: nim
proc representObject*[T: uint8|uint16|uint32|uint64](
value: T, ts: TagStyle, c: SerializationContext):
RawYamlStream {.raises: [].} =
result = iterator(): YamlStreamEvent =
yield scalarEvent($value, presentTag(T, ts), yAnchorNone)
The following example for representing to a YAML non-scalar is the actual
implementation of representing ``seq`` types:
.. code-block:: nim
proc representObject*[T](value: seq[T], ts: TagStyle,
c: SerializationContext): RawYamlStream {.raises: [].} =
result = iterator(): YamlStreamEvent =
let childTagStyle = if ts == tsRootOnly: tsNone else: ts
yield YamlStreamEvent(kind: yamlStartSequence,
seqTag: presentTag(seq[T], ts),
seqAnchor: yAnchorNone)
for item in value:
var events = representObject(item, childTagStyle, c)
while true:
let event = events()
if finished(events): break
yield event
yield YamlStreamEvent(kind: yamlEndSequence)

View File

@ -87,6 +87,11 @@ dt a:before {
#testingground pre {
font-size: small;
}
#testingground #style-options {
margin-top: 10px;
width: 100%;
text-align: center;
}
#testingground .style-option {
display: inline-block;
margin-right: 20px;

View File

@ -25,18 +25,20 @@
<h1 class="title">Testing Ground</h1>
<p>Input is being processed on the fly by a friendly web service and
Output is updated as you type.</p>
<section id="testingground">
<table style="width: 100%; table-layout: fixed">
<thead>
<tr>
<th>Input</th>
<th>Output</th>
</tr>
</thead>
<tbody>
<tr>
<td style="width: 50%; height: 400px; vertical-align: top;">
<textarea id="yaml-input" style="width: 100%; height: 100%">
</div>
</article>
<section id="testingground">
<table style="width: 100%; table-layout: fixed">
<thead>
<tr>
<th>Input</th>
<th>Output</th>
</tr>
</thead>
<tbody>
<tr>
<td style="width: 50%; height: 550px; vertical-align: top;">
<textarea id="yaml-input" style="width: 100%; height: 100%">
- test some
- {YAML: here}
- foo: bar
@ -47,42 +49,40 @@
: !!bool yes
? reference to anchor
: *a</textarea>
</td>
<td style="width: 50%; vertical-align: top; height: 400px; padding-left: 10px">
<div style="width:100%; height:100%; overflow: scroll">
<pre id="yaml-output" style="width: 100%"/>
</div>
</td>
</tr>
</tbody>
</table>
<div id="style-options">
<p>Output style:</p>
<div class="style-option">
<input type="radio" name="style" id="style-minimal" value="minimal"/>
<label for="style-minimal">Minimal</label>
</div>
<div class="style-option">
<input type="radio" name="style" id="style-default" value="default"/>
<label for="style-default">Default</label>
</div>
<div class="style-option">
<input type="radio" name="style" id="style-canonical" value="canonical" checked="checked"/>
<label for="style-canonical">Canonical</label>
</div>
<div class="style-option">
<input type="radio" name="style" id="style-block" value="block"/>
<label for="style-block">Block Only</label>
</div>
<div class="style-option">
<input type="radio" name="style" id="style-json" value="json"/>
<label for="style-json">JSON</label>
</div>
</div>
</section>
</td>
<td style="width: 50%; vertical-align: top; height: 550px; padding-left: 10px">
<div style="width:100%; height:100%; overflow: scroll">
<pre id="yaml-output" style="width: 100%"/>
</div>
</td>
</tr>
</tbody>
</table>
<div id="style-options">
<div class="style-option">Output style:</div>
<div class="style-option">
<input type="radio" name="style" id="style-minimal" value="minimal"/>
<label for="style-minimal">Minimal</label>
</div>
<div class="style-option">
<input type="radio" name="style" id="style-default" value="default"/>
<label for="style-default">Default</label>
</div>
<div class="style-option">
<input type="radio" name="style" id="style-canonical" value="canonical" checked="checked"/>
<label for="style-canonical">Canonical</label>
</div>
<div class="style-option">
<input type="radio" name="style" id="style-block" value="block"/>
<label for="style-block">Block Only</label>
</div>
<div class="style-option">
<input type="radio" name="style" id="style-json" value="json"/>
<label for="style-json">JSON</label>
</div>
</div>
</article>
<script type="text/javascript">
</section>
<script type="text/javascript">
function setTextContent(element, text) {
while (element.firstChild!==null) {
element.removeChild(element.firstChild); // remove all existing content
@ -137,6 +137,6 @@
radios[i].onclick = parse;
}
parse();
</script>
</body>
</script>
</body>
</html>