Simplified the Constructor API.

This commit is contained in:
Ferdinand Majerech 2011-11-17 23:53:24 +01:00
parent ab154480fb
commit fbc962623d
16 changed files with 217 additions and 297 deletions

Binary file not shown.

View file

@ -3,13 +3,13 @@ Custom YAML data types
======================
Sometimes you need to serialize complex data types such as classes. To do this
you could use plain nodes such as mappings with class data members. However,
YAML supports custom types with identifiers called *tags*. That is the topic of
this tutorial.
you could use plain nodes such as mappings with class data members. YAML also
supports custom types with identifiers called *tags*. That is the topic of this
tutorial.
Each YAML node has a tag specifying its type. For instance: strings use the tag
``tag:yaml.org,2002:str``. Tags of most default types are *implicitly resolved*
during parsing, so you don't need to specify tag for each float, integer, etc.
during parsing - you don't need to specify tag for each float, integer, etc.
D:YAML can also implicitly resolve custom tags, as we will show later.
@ -24,7 +24,7 @@ the *addConstructorXXX()* methods, where *XXX* is *Scalar*, *Sequence* or
*Mapping*. *Constructor* is then passed to *Loader*, which parses YAML input.
Struct types have no specific requirements for YAML support. Class types should
define the *opEquals()* operator, as this is used in equality comparisons of
define the *opEquals()* operator - this is used in equality comparisons of
nodes. Default class *opEquals()* compares references, which means two identical
objects might be considered unequal. (Default struct *opEquals()* compares
byte-by-byte, sometimes you might want to override that as well.)
@ -41,24 +41,26 @@ following struct:
ubyte blue;
}
First, we need a function to construct our data type. It must take two *Mark*
structs, which store position of the node in the file, and a reference to *Node*
to construct from. The node is guaranteed to contain either a *string*, an array
of *Node* or of *Node.Pair*, depending on whether we're constructing our value
from a scalar, sequence, or mapping, respectively. In this tutorial, we have
functions to construct a color from a scalar, using CSS-like format, RRGGBB, or
from a mapping, where we use the following format: {r:RRR, g:GGG, b:BBB} . Code
of these functions:
First, we need a function to construct our data type. The function will take a
reference to *Node* to construct from. The node is guaranteed to contain either
a *string*, an array of *Node* or of *Node.Pair*, depending on whether we're
constructing our value from a scalar, sequence, or mapping, respectively.
If this function throws any exception, D:YAML handles it and adds its message
to a *YAMLException* that will be thrown when loading the file.
In this tutorial, we have functions to construct a color from a scalar, using
CSS-like format, RRGGBB, or from a mapping, where we use the following format:
{r:RRR, g:GGG, b:BBB} . Code of these functions:
.. code-block:: d
Color constructColorScalar(Mark start, Mark end, ref Node node)
Color constructColorScalar(ref Node node)
{
string value = node.as!string;
if(value.length != 6)
{
throw new ConstructorException("Invalid color: " ~ value, start, end);
throw new Exception("Invalid color: " ~ value);
}
//We don't need to check for uppercase chars this way.
value = value.toLower();
@ -68,7 +70,7 @@ of these functions:
{
if(!std.ascii.isHexDigit(c))
{
throw new ConstructorException("Invalid color: " ~ value, start, end);
throw new Exception("Invalid color: " ~ value);
}
if(std.ascii.isDigit(c))
@ -86,21 +88,16 @@ of these functions:
return result;
}
Color constructColorMapping(Mark start, Mark end, ref Node node)
Color constructColorMapping(ref Node node)
{
ubyte r,g,b;
//Might throw if a value is missing is not an integer, or is out of range.
try
{
r = node["r"].as!ubyte;
g = node["g"].as!ubyte;
b = node["b"].as!ubyte;
}
catch(NodeException e)
{
throw new ConstructorException("Invalid color: " ~ e.msg, start, end);
}
//If this happens, D:YAML will handle the exception and use its message
//in a YAMLException thrown when loading.
r = node["r"].as!ubyte;
g = node["g"].as!ubyte;
b = node["b"].as!ubyte;
return Color(cast(ubyte)r, cast(ubyte)g, cast(ubyte)b);
}

View file

@ -95,14 +95,20 @@
</table></div>
</dd>
<dt class="d_decl">void <a name="addConstructorScalar"></a><span class="ddoc_psymbol">addConstructorScalar</span>(T)(in string <b>tag</b>, T function(Mark, Mark, ref Node) <b>ctor</b>);
<dt class="d_decl">void <a name="addConstructorScalar"></a><span class="ddoc_psymbol">addConstructorScalar</span>(T)(in string <b>tag</b>, T function(ref Node) <b>ctor</b>);
</dt>
<dd><p>Add a constructor function from scalar.
</p>
<p>The function must take two Marks (start and end positions of
the node in file) and a reference to Node to construct from.
<p>The function must take a reference to Node to construct from.
The node contains a string for scalars, Node[] for sequences and
Node.Pair[] for mappings.
<br>
Any exception thrown by this function will be caught by D:YAML and
its message will be added to a YAMLException that will also tell the
user which type failed to construct, and position in the file.
<br>
The value returned by this function will be stored in the resulting node.
<br>
@ -124,20 +130,13 @@
<span class="d_keyword">int</span> x, y, z;
}
MyStruct constructMyStructScalar(Mark start, Mark end, <span class="d_keyword">ref</span> Node node)
MyStruct constructMyStructScalar(<span class="d_keyword">ref</span> Node node)
{
<span class="d_comment">//Guaranteed to be string as we construct from scalar.
</span> <span class="d_comment">//!mystruct x:y:z
</span> <span class="d_keyword">auto</span> parts = node.as!string().split(<span class="d_string">":"</span>);
<span class="d_keyword">try</span>
{
<span class="d_keyword">return</span> MyStruct(to!<span class="d_keyword">int</span>(parts[0]), to!<span class="d_keyword">int</span>(parts[1]), to!<span class="d_keyword">int</span>(parts[2]));
}
<span class="d_keyword">catch</span>(Exception e)
{
<span class="d_keyword">throw</span> <span class="d_keyword">new</span> ConstructorException(<span class="d_string">"Could not construct MyStruct: "</span> ~ e.msg,
start, end);
}
<span class="d_comment">//If this throws, the D:YAML will handle it and throw a YAMLException.
</span> <span class="d_keyword">return</span> MyStruct(to!<span class="d_keyword">int</span>(parts[0]), to!<span class="d_keyword">int</span>(parts[1]), to!<span class="d_keyword">int</span>(parts[2]));
}
<span class="d_keyword">void</span> main()
@ -152,7 +151,7 @@
</p>
</dd>
<dt class="d_decl">void <a name="addConstructorSequence"></a><span class="ddoc_psymbol">addConstructorSequence</span>(T)(in string <b>tag</b>, T function(Mark, Mark, ref Node) <b>ctor</b>);
<dt class="d_decl">void <a name="addConstructorSequence"></a><span class="ddoc_psymbol">addConstructorSequence</span>(T)(in string <b>tag</b>, T function(ref Node) <b>ctor</b>);
</dt>
<dd><p>Add a constructor function from sequence.
</p>
@ -169,19 +168,11 @@
<span class="d_keyword">int</span> x, y, z;
}
MyStruct constructMyStructSequence(Mark start, Mark end, <span class="d_keyword">ref</span> Node node)
MyStruct constructMyStructSequence(<span class="d_keyword">ref</span> Node node)
{
<span class="d_comment">//node is guaranteed to be sequence.
</span> <span class="d_comment">//!mystruct [x, y, z]
</span> <span class="d_keyword">try</span>
{
<span class="d_keyword">return</span> MyStruct(node[0].as!<span class="d_keyword">int</span>, node[1].as!<span class="d_keyword">int</span>, node[2].as!<span class="d_keyword">int</span>);
}
<span class="d_keyword">catch</span>(NodeException e)
{
<span class="d_keyword">throw</span> <span class="d_keyword">new</span> ConstructorException(<span class="d_string">"Could not construct MyStruct: "</span> ~ e.msg,
start, end);
}
</span> <span class="d_keyword">return</span> MyStruct(node[0].as!<span class="d_keyword">int</span>, node[1].as!<span class="d_keyword">int</span>, node[2].as!<span class="d_keyword">int</span>);
}
<span class="d_keyword">void</span> main()
@ -196,7 +187,7 @@
</p>
</dd>
<dt class="d_decl">void <a name="addConstructorMapping"></a><span class="ddoc_psymbol">addConstructorMapping</span>(T)(in string <b>tag</b>, T function(Mark, Mark, ref Node) <b>ctor</b>);
<dt class="d_decl">void <a name="addConstructorMapping"></a><span class="ddoc_psymbol">addConstructorMapping</span>(T)(in string <b>tag</b>, T function(ref Node) <b>ctor</b>);
</dt>
<dd><p>Add a constructor function from a mapping.
</p>
@ -213,19 +204,11 @@
<span class="d_keyword">int</span> x, y, z;
}
MyStruct constructMyStructMapping(Mark start, Mark end, <span class="d_keyword">ref</span> Node node)
MyStruct constructMyStructMapping(<span class="d_keyword">ref</span> Node node)
{
<span class="d_comment">//node is guaranteed to be mapping.
</span> <span class="d_comment">//!mystruct {"x": x, "y": y, "z": z}
</span> <span class="d_keyword">try</span>
{
<span class="d_keyword">return</span> MyStruct(node[<span class="d_string">"x"</span>].as!<span class="d_keyword">int</span>, node[<span class="d_string">"y"</span>].as!<span class="d_keyword">int</span>, node[<span class="d_string">"z"</span>].as!<span class="d_keyword">int</span>);
}
<span class="d_keyword">catch</span>(NodeException e)
{
<span class="d_keyword">throw</span> <span class="d_keyword">new</span> ConstructorException(<span class="d_string">"Could not construct MyStruct: "</span> ~ e.msg,
start, end);
}
</span> <span class="d_keyword">return</span> MyStruct(node[<span class="d_string">"x"</span>].as!<span class="d_keyword">int</span>, node[<span class="d_string">"y"</span>].as!<span class="d_keyword">int</span>, node[<span class="d_string">"z"</span>].as!<span class="d_keyword">int</span>);
}
<span class="d_keyword">void</span> main()
@ -242,72 +225,72 @@
</dd>
</dl>
</dd>
<dt class="d_decl">YAMLNull <a name="constructNull"></a><span class="ddoc_psymbol">constructNull</span>(Mark <b>start</b>, Mark <b>end</b>, ref Node <b>node</b>);
<dt class="d_decl">YAMLNull <a name="constructNull"></a><span class="ddoc_psymbol">constructNull</span>(ref Node <b>node</b>);
</dt>
<dd><p>Construct a <b>null</b> <b>node</b>.</p>
</dd>
<dt class="d_decl">YAMLMerge <a name="constructMerge"></a><span class="ddoc_psymbol">constructMerge</span>(Mark <b>start</b>, Mark <b>end</b>, ref Node <b>node</b>);
<dt class="d_decl">YAMLMerge <a name="constructMerge"></a><span class="ddoc_psymbol">constructMerge</span>(ref Node <b>node</b>);
</dt>
<dd><p>Construct a merge <b>node</b> - a <b>node</b> that merges another <b>node</b> into a mapping.</p>
</dd>
<dt class="d_decl">bool <a name="constructBool"></a><span class="ddoc_psymbol">constructBool</span>(Mark <b>start</b>, Mark <b>end</b>, ref Node <b>node</b>);
<dt class="d_decl">bool <a name="constructBool"></a><span class="ddoc_psymbol">constructBool</span>(ref Node <b>node</b>);
</dt>
<dd><p>Construct a boolean <b>node</b>.</p>
</dd>
<dt class="d_decl">long <a name="constructLong"></a><span class="ddoc_psymbol">constructLong</span>(Mark <b>start</b>, Mark <b>end</b>, ref Node <b>node</b>);
<dt class="d_decl">long <a name="constructLong"></a><span class="ddoc_psymbol">constructLong</span>(ref Node <b>node</b>);
</dt>
<dd><p>Construct an integer (long) <b>node</b>.</p>
</dd>
<dt class="d_decl">real <a name="constructReal"></a><span class="ddoc_psymbol">constructReal</span>(Mark <b>start</b>, Mark <b>end</b>, ref Node <b>node</b>);
<dt class="d_decl">real <a name="constructReal"></a><span class="ddoc_psymbol">constructReal</span>(ref Node <b>node</b>);
</dt>
<dd><p>Construct a floating point (real) <b>node</b>.</p>
</dd>
<dt class="d_decl">ubyte[] <a name="constructBinary"></a><span class="ddoc_psymbol">constructBinary</span>(Mark <b>start</b>, Mark <b>end</b>, ref Node <b>node</b>);
<dt class="d_decl">ubyte[] <a name="constructBinary"></a><span class="ddoc_psymbol">constructBinary</span>(ref Node <b>node</b>);
</dt>
<dd><p>Construct a binary (base64) <b>node</b>.</p>
</dd>
<dt class="d_decl">SysTime <a name="constructTimestamp"></a><span class="ddoc_psymbol">constructTimestamp</span>(Mark <b>start</b>, Mark <b>end</b>, ref Node <b>node</b>);
<dt class="d_decl">SysTime <a name="constructTimestamp"></a><span class="ddoc_psymbol">constructTimestamp</span>(ref Node <b>node</b>);
</dt>
<dd><p>Construct a timestamp (SysTime) <b>node</b>.</p>
</dd>
<dt class="d_decl">string <a name="constructString"></a><span class="ddoc_psymbol">constructString</span>(Mark <b>start</b>, Mark <b>end</b>, ref Node <b>node</b>);
<dt class="d_decl">string <a name="constructString"></a><span class="ddoc_psymbol">constructString</span>(ref Node <b>node</b>);
</dt>
<dd><p>Construct a string <b>node</b>.</p>
</dd>
<dt class="d_decl">Pair[] <a name="getPairs"></a><span class="ddoc_psymbol">getPairs</span>(string <b>type</b>, Mark <b>start</b>, Mark <b>end</b>, Node[] <b>nodes</b>);
<dt class="d_decl">Pair[] <a name="getPairs"></a><span class="ddoc_psymbol">getPairs</span>(string <b>type</b>, Node[] <b>nodes</b>);
</dt>
<dd><p>Convert a sequence of single-element mappings into a sequence of pairs.</p>
</dd>
<dt class="d_decl">Pair[] <a name="constructOrderedMap"></a><span class="ddoc_psymbol">constructOrderedMap</span>(Mark <b>start</b>, Mark <b>end</b>, ref Node <b>node</b>);
<dt class="d_decl">Pair[] <a name="constructOrderedMap"></a><span class="ddoc_psymbol">constructOrderedMap</span>(ref Node <b>node</b>);
</dt>
<dd><p>Construct an ordered map (ordered sequence of key:value pairs without duplicates) <b>node</b>.</p>
</dd>
<dt class="d_decl">Pair[] <a name="constructPairs"></a><span class="ddoc_psymbol">constructPairs</span>(Mark <b>start</b>, Mark <b>end</b>, ref Node <b>node</b>);
<dt class="d_decl">Pair[] <a name="constructPairs"></a><span class="ddoc_psymbol">constructPairs</span>(ref Node <b>node</b>);
</dt>
<dd><p>Construct a pairs (ordered sequence of key: value pairs allowing duplicates) <b>node</b>.</p>
</dd>
<dt class="d_decl">Node[] <a name="constructSet"></a><span class="ddoc_psymbol">constructSet</span>(Mark <b>start</b>, Mark <b>end</b>, ref Node <b>node</b>);
<dt class="d_decl">Node[] <a name="constructSet"></a><span class="ddoc_psymbol">constructSet</span>(ref Node <b>node</b>);
</dt>
<dd><p>Construct a set <b>node</b>.</p>
</dd>
<dt class="d_decl">Node[] <a name="constructSequence"></a><span class="ddoc_psymbol">constructSequence</span>(Mark <b>start</b>, Mark <b>end</b>, ref Node <b>node</b>);
<dt class="d_decl">Node[] <a name="constructSequence"></a><span class="ddoc_psymbol">constructSequence</span>(ref Node <b>node</b>);
</dt>
<dd><p>Construct a sequence (array) <b>node</b>.</p>
</dd>
<dt class="d_decl">Pair[] <a name="constructMap"></a><span class="ddoc_psymbol">constructMap</span>(Mark <b>start</b>, Mark <b>end</b>, ref Node <b>node</b>);
<dt class="d_decl">Pair[] <a name="constructMap"></a><span class="ddoc_psymbol">constructMap</span>(ref Node <b>node</b>);
</dt>
<dd><p>Construct an unordered map (unordered set of key: value pairs without duplicates) <b>node</b>.</p>

View file

@ -138,7 +138,7 @@ struct appears in Phobos.</p>
</div>
<div class="footer">
&copy; Copyright 2011, Ferdinand Majerech. Based on PyYAML http://www.pyyaml.org by Kirill Simonov.
Last updated on Nov 16, 2011.
Last updated on Nov 17, 2011.
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.0.7.
</div>
</body>

View file

@ -104,7 +104,7 @@
</div>
<div class="footer">
&copy; Copyright 2011, Ferdinand Majerech. Based on PyYAML http://www.pyyaml.org by Kirill Simonov.
Last updated on Nov 16, 2011.
Last updated on Nov 17, 2011.
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.0.7.
</div>
</body>

View file

@ -87,7 +87,7 @@
</div>
<div class="footer">
&copy; Copyright 2011, Ferdinand Majerech. Based on PyYAML http://www.pyyaml.org by Kirill Simonov.
Last updated on Nov 16, 2011.
Last updated on Nov 17, 2011.
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.0.7.
</div>
</body>

File diff suppressed because one or more lines are too long

View file

@ -48,12 +48,12 @@
<div class="section" id="custom-yaml-data-types">
<h1>Custom YAML data types<a class="headerlink" href="#custom-yaml-data-types" title="Permalink to this headline"></a></h1>
<p>Sometimes you need to serialize complex data types such as classes. To do this
you could use plain nodes such as mappings with class data members. However,
YAML supports custom types with identifiers called <em>tags</em>. That is the topic of
this tutorial.</p>
you could use plain nodes such as mappings with class data members. YAML also
supports custom types with identifiers called <em>tags</em>. That is the topic of this
tutorial.</p>
<p>Each YAML node has a tag specifying its type. For instance: strings use the tag
<tt class="docutils literal"><span class="pre">tag:yaml.org,2002:str</span></tt>. Tags of most default types are <em>implicitly resolved</em>
during parsing, so you don&#8217;t need to specify tag for each float, integer, etc.
during parsing - you don&#8217;t need to specify tag for each float, integer, etc.
D:YAML can also implicitly resolve custom tags, as we will show later.</p>
<div class="section" id="constructor">
<h2>Constructor<a class="headerlink" href="#constructor" title="Permalink to this headline"></a></h2>
@ -63,7 +63,7 @@ functions to process each supported tag. These are supplied by the user using
the <em>addConstructorXXX()</em> methods, where <em>XXX</em> is <em>Scalar</em>, <em>Sequence</em> or
<em>Mapping</em>. <em>Constructor</em> is then passed to <em>Loader</em>, which parses YAML input.</p>
<p>Struct types have no specific requirements for YAML support. Class types should
define the <em>opEquals()</em> operator, as this is used in equality comparisons of
define the <em>opEquals()</em> operator - this is used in equality comparisons of
nodes. Default class <em>opEquals()</em> compares references, which means two identical
objects might be considered unequal. (Default struct <em>opEquals()</em> compares
byte-by-byte, sometimes you might want to override that as well.)</p>
@ -77,21 +77,22 @@ following struct:</p>
<span class="p">}</span>
</pre></div>
</div>
<p>First, we need a function to construct our data type. It must take two <em>Mark</em>
structs, which store position of the node in the file, and a reference to <em>Node</em>
to construct from. The node is guaranteed to contain either a <em>string</em>, an array
of <em>Node</em> or of <em>Node.Pair</em>, depending on whether we&#8217;re constructing our value
from a scalar, sequence, or mapping, respectively. In this tutorial, we have
functions to construct a color from a scalar, using CSS-like format, RRGGBB, or
from a mapping, where we use the following format: {r:RRR, g:GGG, b:BBB} . Code
of these functions:</p>
<div class="highlight-d"><div class="highlight"><pre><span class="n">Color</span> <span class="n">constructColorScalar</span><span class="p">(</span><span class="n">Mark</span> <span class="n">start</span><span class="p">,</span> <span class="n">Mark</span> <span class="n">end</span><span class="p">,</span> <span class="k">ref</span> <span class="n">Node</span> <span class="n">node</span><span class="p">)</span>
<p>First, we need a function to construct our data type. The function will take a
reference to <em>Node</em> to construct from. The node is guaranteed to contain either
a <em>string</em>, an array of <em>Node</em> or of <em>Node.Pair</em>, depending on whether we&#8217;re
constructing our value from a scalar, sequence, or mapping, respectively.
If this function throws any exception, D:YAML handles it and adds its message
to a <em>YAMLException</em> that will be thrown when loading the file.</p>
<p>In this tutorial, we have functions to construct a color from a scalar, using
CSS-like format, RRGGBB, or from a mapping, where we use the following format:
{r:RRR, g:GGG, b:BBB} . Code of these functions:</p>
<div class="highlight-d"><div class="highlight"><pre><span class="n">Color</span> <span class="n">constructColorScalar</span><span class="p">(</span><span class="k">ref</span> <span class="n">Node</span> <span class="n">node</span><span class="p">)</span>
<span class="p">{</span>
<span class="nb">string</span> <span class="n">value</span> <span class="p">=</span> <span class="n">node</span><span class="p">.</span><span class="n">as</span><span class="p">!</span><span class="nb">string</span><span class="p">;</span>
<span class="k">if</span><span class="p">(</span><span class="n">value</span><span class="p">.</span><span class="n">length</span> <span class="p">!=</span> <span class="mi">6</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="n">ConstructorException</span><span class="p">(</span><span class="s">&quot;Invalid color: &quot;</span> <span class="p">~</span> <span class="n">value</span><span class="p">,</span> <span class="n">start</span><span class="p">,</span> <span class="n">end</span><span class="p">);</span>
<span class="k">throw</span> <span class="k">new</span> <span class="n">Exception</span><span class="p">(</span><span class="s">&quot;Invalid color: &quot;</span> <span class="p">~</span> <span class="n">value</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">//We don&#39;t need to check for uppercase chars this way.</span>
<span class="n">value</span> <span class="p">=</span> <span class="n">value</span><span class="p">.</span><span class="n">toLower</span><span class="p">();</span>
@ -101,7 +102,7 @@ of these functions:</p>
<span class="p">{</span>
<span class="k">if</span><span class="p">(!</span><span class="n">std</span><span class="p">.</span><span class="n">ascii</span><span class="p">.</span><span class="n">isHexDigit</span><span class="p">(</span><span class="n">c</span><span class="p">))</span>
<span class="p">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="n">ConstructorException</span><span class="p">(</span><span class="s">&quot;Invalid color: &quot;</span> <span class="p">~</span> <span class="n">value</span><span class="p">,</span> <span class="n">start</span><span class="p">,</span> <span class="n">end</span><span class="p">);</span>
<span class="k">throw</span> <span class="k">new</span> <span class="n">Exception</span><span class="p">(</span><span class="s">&quot;Invalid color: &quot;</span> <span class="p">~</span> <span class="n">value</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">if</span><span class="p">(</span><span class="n">std</span><span class="p">.</span><span class="n">ascii</span><span class="p">.</span><span class="n">isDigit</span><span class="p">(</span><span class="n">c</span><span class="p">))</span>
@ -119,21 +120,16 @@ of these functions:</p>
<span class="k">return</span> <span class="n">result</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">Color</span> <span class="n">constructColorMapping</span><span class="p">(</span><span class="n">Mark</span> <span class="n">start</span><span class="p">,</span> <span class="n">Mark</span> <span class="n">end</span><span class="p">,</span> <span class="k">ref</span> <span class="n">Node</span> <span class="n">node</span><span class="p">)</span>
<span class="n">Color</span> <span class="n">constructColorMapping</span><span class="p">(</span><span class="k">ref</span> <span class="n">Node</span> <span class="n">node</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">ubyte</span> <span class="n">r</span><span class="p">,</span><span class="n">g</span><span class="p">,</span><span class="n">b</span><span class="p">;</span>
<span class="c1">//Might throw if a value is missing is not an integer, or is out of range.</span>
<span class="k">try</span>
<span class="p">{</span>
<span class="n">r</span> <span class="p">=</span> <span class="n">node</span><span class="p">[</span><span class="s">&quot;r&quot;</span><span class="p">].</span><span class="n">as</span><span class="p">!</span><span class="kt">ubyte</span><span class="p">;</span>
<span class="n">g</span> <span class="p">=</span> <span class="n">node</span><span class="p">[</span><span class="s">&quot;g&quot;</span><span class="p">].</span><span class="n">as</span><span class="p">!</span><span class="kt">ubyte</span><span class="p">;</span>
<span class="n">b</span> <span class="p">=</span> <span class="n">node</span><span class="p">[</span><span class="s">&quot;b&quot;</span><span class="p">].</span><span class="n">as</span><span class="p">!</span><span class="kt">ubyte</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">catch</span><span class="p">(</span><span class="n">NodeException</span> <span class="n">e</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="n">ConstructorException</span><span class="p">(</span><span class="s">&quot;Invalid color: &quot;</span> <span class="p">~</span> <span class="n">e</span><span class="p">.</span><span class="n">msg</span><span class="p">,</span> <span class="n">start</span><span class="p">,</span> <span class="n">end</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">//If this happens, D:YAML will handle the exception and use its message</span>
<span class="c1">//in a YAMLException thrown when loading.</span>
<span class="n">r</span> <span class="p">=</span> <span class="n">node</span><span class="p">[</span><span class="s">&quot;r&quot;</span><span class="p">].</span><span class="n">as</span><span class="p">!</span><span class="kt">ubyte</span><span class="p">;</span>
<span class="n">g</span> <span class="p">=</span> <span class="n">node</span><span class="p">[</span><span class="s">&quot;g&quot;</span><span class="p">].</span><span class="n">as</span><span class="p">!</span><span class="kt">ubyte</span><span class="p">;</span>
<span class="n">b</span> <span class="p">=</span> <span class="n">node</span><span class="p">[</span><span class="s">&quot;b&quot;</span><span class="p">].</span><span class="n">as</span><span class="p">!</span><span class="kt">ubyte</span><span class="p">;</span>
<span class="k">return</span> <span class="n">Color</span><span class="p">(</span><span class="k">cast</span><span class="p">(</span><span class="kt">ubyte</span><span class="p">)</span><span class="n">r</span><span class="p">,</span> <span class="k">cast</span><span class="p">(</span><span class="kt">ubyte</span><span class="p">)</span><span class="n">g</span><span class="p">,</span> <span class="k">cast</span><span class="p">(</span><span class="kt">ubyte</span><span class="p">)</span><span class="n">b</span><span class="p">);</span>
<span class="p">}</span>
@ -368,7 +364,7 @@ directory of the D:YAML package.</p>
</div>
<div class="footer">
&copy; Copyright 2011, Ferdinand Majerech. Based on PyYAML http://www.pyyaml.org by Kirill Simonov.
Last updated on Nov 16, 2011.
Last updated on Nov 17, 2011.
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.0.7.
</div>
</body>

View file

@ -237,7 +237,7 @@ example in the <tt class="docutils literal"><span class="pre">example/getting_st
</div>
<div class="footer">
&copy; Copyright 2011, Ferdinand Majerech. Based on PyYAML http://www.pyyaml.org by Kirill Simonov.
Last updated on Nov 16, 2011.
Last updated on Nov 17, 2011.
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.0.7.
</div>
</body>

View file

@ -330,7 +330,7 @@ Some of these might change in the future (especially !!map and !!set).</p>
</div>
<div class="footer">
&copy; Copyright 2011, Ferdinand Majerech. Based on PyYAML http://www.pyyaml.org by Kirill Simonov.
Last updated on Nov 16, 2011.
Last updated on Nov 17, 2011.
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.0.7.
</div>
</body>

View file

@ -3,13 +3,13 @@ Custom YAML data types
======================
Sometimes you need to serialize complex data types such as classes. To do this
you could use plain nodes such as mappings with class data members. However,
YAML supports custom types with identifiers called *tags*. That is the topic of
this tutorial.
you could use plain nodes such as mappings with class data members. YAML also
supports custom types with identifiers called *tags*. That is the topic of this
tutorial.
Each YAML node has a tag specifying its type. For instance: strings use the tag
``tag:yaml.org,2002:str``. Tags of most default types are *implicitly resolved*
during parsing, so you don't need to specify tag for each float, integer, etc.
during parsing - you don't need to specify tag for each float, integer, etc.
D:YAML can also implicitly resolve custom tags, as we will show later.
@ -24,7 +24,7 @@ the *addConstructorXXX()* methods, where *XXX* is *Scalar*, *Sequence* or
*Mapping*. *Constructor* is then passed to *Loader*, which parses YAML input.
Struct types have no specific requirements for YAML support. Class types should
define the *opEquals()* operator, as this is used in equality comparisons of
define the *opEquals()* operator - this is used in equality comparisons of
nodes. Default class *opEquals()* compares references, which means two identical
objects might be considered unequal. (Default struct *opEquals()* compares
byte-by-byte, sometimes you might want to override that as well.)
@ -41,24 +41,26 @@ following struct:
ubyte blue;
}
First, we need a function to construct our data type. It must take two *Mark*
structs, which store position of the node in the file, and a reference to *Node*
to construct from. The node is guaranteed to contain either a *string*, an array
of *Node* or of *Node.Pair*, depending on whether we're constructing our value
from a scalar, sequence, or mapping, respectively. In this tutorial, we have
functions to construct a color from a scalar, using CSS-like format, RRGGBB, or
from a mapping, where we use the following format: {r:RRR, g:GGG, b:BBB} . Code
of these functions:
First, we need a function to construct our data type. The function will take a
reference to *Node* to construct from. The node is guaranteed to contain either
a *string*, an array of *Node* or of *Node.Pair*, depending on whether we're
constructing our value from a scalar, sequence, or mapping, respectively.
If this function throws any exception, D:YAML handles it and adds its message
to a *YAMLException* that will be thrown when loading the file.
In this tutorial, we have functions to construct a color from a scalar, using
CSS-like format, RRGGBB, or from a mapping, where we use the following format:
{r:RRR, g:GGG, b:BBB} . Code of these functions:
.. code-block:: d
Color constructColorScalar(Mark start, Mark end, ref Node node)
Color constructColorScalar(ref Node node)
{
string value = node.as!string;
if(value.length != 6)
{
throw new ConstructorException("Invalid color: " ~ value, start, end);
throw new Exception("Invalid color: " ~ value);
}
//We don't need to check for uppercase chars this way.
value = value.toLower();
@ -68,7 +70,7 @@ of these functions:
{
if(!std.ascii.isHexDigit(c))
{
throw new ConstructorException("Invalid color: " ~ value, start, end);
throw new Exception("Invalid color: " ~ value);
}
if(std.ascii.isDigit(c))
@ -86,21 +88,16 @@ of these functions:
return result;
}
Color constructColorMapping(Mark start, Mark end, ref Node node)
Color constructColorMapping(ref Node node)
{
ubyte r,g,b;
//Might throw if a value is missing is not an integer, or is out of range.
try
{
r = node["r"].as!ubyte;
g = node["g"].as!ubyte;
b = node["b"].as!ubyte;
}
catch(NodeException e)
{
throw new ConstructorException("Invalid color: " ~ e.msg, start, end);
}
//If this happens, D:YAML will handle the exception and use its message
//in a YAMLException thrown when loading.
r = node["r"].as!ubyte;
g = node["g"].as!ubyte;
b = node["b"].as!ubyte;
return Color(cast(ubyte)r, cast(ubyte)g, cast(ubyte)b);
}

View file

@ -34,7 +34,7 @@ import dyaml.style;
*
* Can be thrown by custom constructor functions.
*/
class ConstructorException : YAMLException
package class ConstructorException : YAMLException
{
/**
* Construct a ConstructorException.
@ -70,11 +70,11 @@ final class Constructor
{
private:
///Constructor functions from scalars.
Node.Value delegate(Mark, Mark, ref Node)[Tag] fromScalar_;
Node.Value delegate(ref Node)[Tag] fromScalar_;
///Constructor functions from sequences.
Node.Value delegate(Mark, Mark, ref Node)[Tag] fromSequence_;
Node.Value delegate(ref Node)[Tag] fromSequence_;
///Constructor functions from mappings.
Node.Value delegate(Mark, Mark, ref Node)[Tag] fromMapping_;
Node.Value delegate(ref Node)[Tag] fromMapping_;
public:
/**
@ -122,10 +122,14 @@ final class Constructor
/**
* Add a constructor function from scalar.
*
* The function must take two Marks (start and end positions of
* the node in file) and a reference to Node to construct from.
* The function must take a reference to Node to construct from.
* The node contains a string for scalars, Node[] for sequences and
* Node.Pair[] for mappings.
* Node.Pair[] for mappings.
*
* Any exception thrown by this function will be caught by D:YAML and
* its message will be added to a YAMLException that will also tell the
* user which type failed to construct, and position in the file.
*
* The value returned by this function will be stored in the resulting node.
*
* Only one constructor function can be set for one tag.
@ -145,20 +149,13 @@ final class Constructor
* int x, y, z;
* }
*
* MyStruct constructMyStructScalar(Mark start, Mark end, ref Node node)
* MyStruct constructMyStructScalar(ref Node node)
* {
* //Guaranteed to be string as we construct from scalar.
* //!mystruct x:y:z
* auto parts = node.as!string().split(":");
* try
* {
* return MyStruct(to!int(parts[0]), to!int(parts[1]), to!int(parts[2]));
* }
* catch(Exception e)
* {
* throw new ConstructorException("Could not construct MyStruct: " ~ e.msg,
* start, end);
* }
* //If this throws, the D:YAML will handle it and throw a YAMLException.
* return MyStruct(to!int(parts[0]), to!int(parts[1]), to!int(parts[2]));
* }
*
* void main()
@ -171,7 +168,7 @@ final class Constructor
* }
* --------------------
*/
void addConstructorScalar(T)(in string tag, T function(Mark, Mark, ref Node) ctor)
void addConstructorScalar(T)(in string tag, T function(ref Node) ctor)
{
const t = Tag(tag);
auto deleg = addConstructor!T(t, ctor);
@ -195,19 +192,11 @@ final class Constructor
* int x, y, z;
* }
*
* MyStruct constructMyStructSequence(Mark start, Mark end, ref Node node)
* MyStruct constructMyStructSequence(ref Node node)
* {
* //node is guaranteed to be sequence.
* //!mystruct [x, y, z]
* try
* {
* return MyStruct(node[0].as!int, node[1].as!int, node[2].as!int);
* }
* catch(NodeException e)
* {
* throw new ConstructorException("Could not construct MyStruct: " ~ e.msg,
* start, end);
* }
* return MyStruct(node[0].as!int, node[1].as!int, node[2].as!int);
* }
*
* void main()
@ -220,7 +209,7 @@ final class Constructor
* }
* --------------------
*/
void addConstructorSequence(T)(in string tag, T function(Mark, Mark, ref Node) ctor)
void addConstructorSequence(T)(in string tag, T function(ref Node) ctor)
{
const t = Tag(tag);
auto deleg = addConstructor!T(t, ctor);
@ -244,19 +233,11 @@ final class Constructor
* int x, y, z;
* }
*
* MyStruct constructMyStructMapping(Mark start, Mark end, ref Node node)
* MyStruct constructMyStructMapping(ref Node node)
* {
* //node is guaranteed to be mapping.
* //!mystruct {"x": x, "y": y, "z": z}
* try
* {
* return MyStruct(node["x"].as!int, node["y"].as!int, node["z"].as!int);
* }
* catch(NodeException e)
* {
* throw new ConstructorException("Could not construct MyStruct: " ~ e.msg,
* start, end);
* }
* return MyStruct(node["x"].as!int, node["y"].as!int, node["z"].as!int);
* }
*
* void main()
@ -269,7 +250,7 @@ final class Constructor
* }
* --------------------
*/
void addConstructorMapping(T)(in string tag, T function(Mark, Mark, ref Node) ctor)
void addConstructorMapping(T)(in string tag, T function(ref Node) ctor)
{
const t = Tag(tag);
auto deleg = addConstructor!T(t, ctor);
@ -295,17 +276,25 @@ final class Constructor
enforce((tag in *delegates!T) !is null,
new Error("No constructor function for tag " ~ tag.get(), start, end));
Node node = Node(value);
static if(is(U : ScalarStyle))
try
{
return Node.rawNode((*delegates!T)[tag](start, end, node), start, tag,
style, CollectionStyle.Invalid);
static if(is(U : ScalarStyle))
{
return Node.rawNode((*delegates!T)[tag](node), start, tag,
style, CollectionStyle.Invalid);
}
else static if(is(U : CollectionStyle))
{
return Node.rawNode((*delegates!T)[tag](node), start, tag,
ScalarStyle.Invalid, style);
}
else static assert(false);
}
else static if(is(U : CollectionStyle))
catch(Exception e)
{
return Node.rawNode((*delegates!T)[tag](start, end, node), start, tag,
ScalarStyle.Invalid, style);
throw new Error("Error constructing " ~ typeid(T).toString()
~ ":\n" ~ e.msg, start, end);
}
else static assert(false);
}
private:
@ -315,7 +304,7 @@ final class Constructor
* Params: tag = Tag for the function to handle.
* ctor = Constructor function.
*/
auto addConstructor(T)(in Tag tag, T function(Mark, Mark, ref Node) ctor)
auto addConstructor(T)(in Tag tag, T function(ref Node) ctor)
{
assert((tag in fromScalar_) is null &&
(tag in fromSequence_) is null &&
@ -323,10 +312,10 @@ final class Constructor
"Constructor function for tag " ~ tag.get ~ " is already "
"specified. Can't specify another one.");
return (Mark s, Mark e, ref Node n)
return (ref Node n)
{
static if(Node.Value.allowed!T){return Node.Value(ctor(s,e,n));}
else {return Node.userValue(ctor(s,e,n));}
static if(Node.Value.allowed!T){return Node.Value(ctor(n));}
else {return Node.userValue(ctor(n));}
};
}
@ -342,30 +331,30 @@ final class Constructor
///Construct a null node.
YAMLNull constructNull(Mark start, Mark end, ref Node node)
YAMLNull constructNull(ref Node node)
{
return YAMLNull();
}
///Construct a merge node - a node that merges another node into a mapping.
YAMLMerge constructMerge(Mark start, Mark end, ref Node node)
YAMLMerge constructMerge(ref Node node)
{
return YAMLMerge();
}
///Construct a boolean node.
bool constructBool(Mark start, Mark end, ref Node node)
bool constructBool(ref Node node)
{
static yes = ["yes", "true", "on"];
static no = ["no", "false", "off"];
string value = node.as!string().toLower();
if(yes.canFind(value)){return true;}
if(no.canFind(value)) {return false;}
throw new Error("Unable to parse boolean value: " ~ value, start, end);
throw new Exception("Unable to parse boolean value: " ~ value);
}
///Construct an integer (long) node.
long constructLong(Mark start, Mark end, ref Node node)
long constructLong(ref Node node)
{
string value = node.as!string().replace("_", "");
const char c = value[0];
@ -375,7 +364,7 @@ long constructLong(Mark start, Mark end, ref Node node)
value = value[1 .. $];
}
enforce(value != "", new Error("Unable to parse float value: " ~ value, start, end));
enforce(value != "", new Exception("Unable to parse float value: " ~ value));
long result;
try
@ -405,7 +394,7 @@ long constructLong(Mark start, Mark end, ref Node node)
}
catch(ConvException e)
{
throw new Error("Unable to parse integer value: " ~ value, start, end);
throw new Exception("Unable to parse integer value: " ~ value);
}
return result;
@ -414,7 +403,7 @@ unittest
{
long getLong(string str)
{
return constructLong(Mark(), Mark(), Node(str));
return constructLong(Node(str));
}
string canonical = "685230";
@ -433,7 +422,7 @@ unittest
}
///Construct a floating point (real) node.
real constructReal(Mark start, Mark end, ref Node node)
real constructReal(ref Node node)
{
string value = node.as!string().replace("_", "").toLower();
const char c = value[0];
@ -444,7 +433,7 @@ real constructReal(Mark start, Mark end, ref Node node)
}
enforce(value != "" && value != "nan" && value != "inf" && value != "-inf",
new Error("Unable to parse float value: " ~ value, start, end));
new Exception("Unable to parse float value: " ~ value));
real result;
try
@ -470,7 +459,7 @@ real constructReal(Mark start, Mark end, ref Node node)
}
catch(ConvException e)
{
throw new Error("Unable to parse float value: " ~ value, start, end);
throw new Exception("Unable to parse float value: " ~ value);
}
return result;
@ -484,7 +473,7 @@ unittest
real getReal(string str)
{
return constructReal(Mark(), Mark(), Node(str));
return constructReal(Node(str));
}
string canonical = "6.8523015e+5";
@ -503,7 +492,7 @@ unittest
}
///Construct a binary (base64) node.
ubyte[] constructBinary(Mark start, Mark end, ref Node node)
ubyte[] constructBinary(ref Node node)
{
string value = node.as!string;
//For an unknown reason, this must be nested to work (compiler bug?).
@ -512,12 +501,12 @@ ubyte[] constructBinary(Mark start, Mark end, ref Node node)
try{return Base64.decode(value.removechars("\n"));}
catch(Exception e)
{
throw new Error("Unable to decode base64 value: " ~ e.msg, start, end);
throw new Exception("Unable to decode base64 value: " ~ e.msg);
}
}
catch(UtfException e)
{
throw new Error("Unable to decode base64 value: " ~ e.msg, start, end);
throw new Exception("Unable to decode base64 value: " ~ e.msg);
}
}
unittest
@ -526,12 +515,12 @@ unittest
char[] buffer;
buffer.length = 256;
string input = cast(string)Base64.encode(test, buffer);
auto value = constructBinary(Mark(), Mark(), Node(input));
auto value = constructBinary(Node(input));
assert(value == test);
}
///Construct a timestamp (SysTime) node.
SysTime constructTimestamp(Mark start, Mark end, ref Node node)
SysTime constructTimestamp(ref Node node)
{
string value = node.as!string;
@ -545,7 +534,7 @@ SysTime constructTimestamp(Mark start, Mark end, ref Node node)
auto matches = match(value, YMDRegexp);
enforce(!matches.empty,
new Error("Unable to parse timestamp value: " ~ value, start, end));
new Exception("Unable to parse timestamp value: " ~ value));
auto captures = matches.front.captures;
const year = to!int(captures[1]);
@ -592,11 +581,11 @@ SysTime constructTimestamp(Mark start, Mark end, ref Node node)
}
catch(ConvException e)
{
throw new Error("Unable to parse timestamp value " ~ value ~ " : " ~ e.msg, start, end);
throw new Exception("Unable to parse timestamp value " ~ value ~ " : " ~ e.msg);
}
catch(DateTimeException e)
{
throw new Error("Invalid timestamp value " ~ value ~ " : " ~ e.msg, start, end);
throw new Exception("Invalid timestamp value " ~ value ~ " : " ~ e.msg);
}
assert(false, "This code should never be reached");
@ -607,7 +596,7 @@ unittest
string timestamp(string value)
{
return constructTimestamp(Mark(), Mark(), Node(value)).toISOString();
return constructTimestamp(Node(value)).toISOString();
}
string canonical = "2001-12-15T02:59:43.1Z";
@ -630,21 +619,21 @@ unittest
}
///Construct a string node.
string constructString(Mark start, Mark end, ref Node node)
string constructString(ref Node node)
{
return node.as!string;
}
///Convert a sequence of single-element mappings into a sequence of pairs.
Node.Pair[] getPairs(string type, Mark start, Mark end, Node[] nodes)
Node.Pair[] getPairs(string type, Node[] nodes)
{
Node.Pair[] pairs;
foreach(ref node; nodes)
{
enforce(node.isMapping && node.length == 1,
new Error("While constructing " ~ type ~
", expected a mapping with single element", start, end));
new Exception("While constructing " ~ type ~
", expected a mapping with single element"));
pairs ~= node.as!(Node.Pair[]);
}
@ -653,9 +642,9 @@ Node.Pair[] getPairs(string type, Mark start, Mark end, Node[] nodes)
}
///Construct an ordered map (ordered sequence of key:value pairs without duplicates) node.
Node.Pair[] constructOrderedMap(Mark start, Mark end, ref Node node)
Node.Pair[] constructOrderedMap(ref Node node)
{
auto pairs = getPairs("ordered map", start, end, node.as!(Node[]));
auto pairs = getPairs("ordered map", node.as!(Node[]));
//TODO: the map here should be replaced with something with deterministic
//memory allocation if possible.
@ -664,7 +653,7 @@ Node.Pair[] constructOrderedMap(Mark start, Mark end, ref Node node)
foreach(ref pair; pairs)
{
enforce((pair.key in map) is null,
new Error("Duplicate entry in an ordered map", start, end));
new Exception("Duplicate entry in an ordered map"));
map[pair.key] = true;
}
clear(map);
@ -701,7 +690,7 @@ unittest
bool hasDuplicates(Node[] nodes)
{
return null !is collectException(constructOrderedMap(Mark(), Mark(), Node(nodes)));
return null !is collectException(constructOrderedMap(Node(nodes)));
}
assert(hasDuplicates(alternateTypes(8) ~ alternateTypes(2)));
@ -713,13 +702,13 @@ unittest
}
///Construct a pairs (ordered sequence of key: value pairs allowing duplicates) node.
Node.Pair[] constructPairs(Mark start, Mark end, ref Node node)
Node.Pair[] constructPairs(ref Node node)
{
return getPairs("pairs", start, end, node.as!(Node[]));
return getPairs("pairs", node.as!(Node[]));
}
///Construct a set node.
Node[] constructSet(Mark start, Mark end, ref Node node)
Node[] constructSet(ref Node node)
{
auto pairs = node.as!(Node.Pair[]);
@ -732,7 +721,7 @@ Node[] constructSet(Mark start, Mark end, ref Node node)
foreach(ref pair; pairs)
{
enforce((pair.key in map) is null,
new Error("Duplicate entry in a set", start, end));
new Exception("Duplicate entry in a set"));
map[pair.key] = 0;
nodes ~= pair.key;
}
@ -773,23 +762,23 @@ unittest
}
assert(null !is collectException
(constructSet(Mark(), Mark(), Node(DuplicatesShort.dup))));
(constructSet(Node(DuplicatesShort.dup))));
assert(null is collectException
(constructSet(Mark(), Mark(), Node(noDuplicatesShort.dup))));
(constructSet(Node(noDuplicatesShort.dup))));
assert(null !is collectException
(constructSet(Mark(), Mark(), Node(DuplicatesLong.dup))));
(constructSet(Node(DuplicatesLong.dup))));
assert(null is collectException
(constructSet(Mark(), Mark(), Node(noDuplicatesLong.dup))));
(constructSet(Node(noDuplicatesLong.dup))));
}
///Construct a sequence (array) node.
Node[] constructSequence(Mark start, Mark end, ref Node node)
Node[] constructSequence(ref Node node)
{
return node.as!(Node[]);
}
///Construct an unordered map (unordered set of key: value _pairs without duplicates) node.
Node.Pair[] constructMap(Mark start, Mark end, ref Node node)
Node.Pair[] constructMap(ref Node node)
{
auto pairs = node.as!(Node.Pair[]);
//TODO: the map here should be replaced with something with deterministic
@ -800,7 +789,7 @@ Node.Pair[] constructMap(Mark start, Mark end, ref Node node)
foreach(ref pair; pairs)
{
enforce((pair.key in map) is null,
new Error("Duplicate entry in a map", start, end));
new Exception("Duplicate entry in a map"));
map[pair.key] = true;
}
return pairs;
@ -818,47 +807,23 @@ struct MyStruct
int x, y, z;
}
MyStruct constructMyStructScalar(Mark start, Mark end, ref Node node)
MyStruct constructMyStructScalar(ref Node node)
{
//Guaranteed to be string as we construct from scalar.
auto parts = node.as!string().split(":");
try
{
return MyStruct(to!int(parts[0]), to!int(parts[1]), to!int(parts[2]));
}
catch(Exception e)
{
throw new ConstructorException("Could not construct MyStruct: " ~ e.msg,
start, end);
}
return MyStruct(to!int(parts[0]), to!int(parts[1]), to!int(parts[2]));
}
MyStruct constructMyStructSequence(Mark start, Mark end, ref Node node)
MyStruct constructMyStructSequence(ref Node node)
{
//node is guaranteed to be sequence.
try
{
return MyStruct(node[0].as!int, node[1].as!int, node[2].as!int);
}
catch(NodeException e)
{
throw new ConstructorException("Could not construct MyStruct: " ~ e.msg,
start, end);
}
return MyStruct(node[0].as!int, node[1].as!int, node[2].as!int);
}
MyStruct constructMyStructMapping(Mark start, Mark end, ref Node node)
MyStruct constructMyStructMapping(ref Node node)
{
//node is guaranteed to be mapping.
try
{
return MyStruct(node["x"].as!int, node["y"].as!int, node["z"].as!int);
}
catch(NodeException e)
{
throw new ConstructorException("Could not construct MyStruct: " ~ e.msg,
start, end);
}
return MyStruct(node["x"].as!int, node["y"].as!int, node["z"].as!int);
}
unittest

View file

@ -10,13 +10,13 @@ struct Color
ubyte blue;
}
Color constructColorScalar(Mark start, Mark end, ref Node node)
Color constructColorScalar(ref Node node)
{
string value = node.as!string;
if(value.length != 6)
{
throw new ConstructorException("Invalid color: " ~ value, start, end);
throw new Exception("Invalid color: " ~ value);
}
//We don't need to check for uppercase chars this way.
value = value.toLower();
@ -26,7 +26,7 @@ Color constructColorScalar(Mark start, Mark end, ref Node node)
{
if(!std.ascii.isHexDigit(c))
{
throw new ConstructorException("Invalid color: " ~ value, start, end);
throw new Exception("Invalid color: " ~ value);
}
if(std.ascii.isDigit(c))
@ -44,21 +44,16 @@ Color constructColorScalar(Mark start, Mark end, ref Node node)
return result;
}
Color constructColorMapping(Mark start, Mark end, ref Node node)
Color constructColorMapping(ref Node node)
{
ubyte r,g,b;
//Might throw if a value is missing is not an integer, or is out of range.
try
{
r = node["r"].as!ubyte;
g = node["g"].as!ubyte;
b = node["b"].as!ubyte;
}
catch(NodeException e)
{
throw new ConstructorException("Invalid color: " ~ e.msg, start, end);
}
//If this happens, D:YAML will handle the exception and use its message
//in a YAMLException thrown when loading.
r = node["r"].as!ubyte;
g = node["g"].as!ubyte;
b = node["b"].as!ubyte;
return Color(cast(ubyte)r, cast(ubyte)g, cast(ubyte)b);
}

View file

@ -10,13 +10,13 @@ struct Color
ubyte blue;
}
Color constructColorScalar(Mark start, Mark end, ref Node node)
Color constructColorScalar(ref Node node)
{
string value = node.as!string;
if(value.length != 6)
{
throw new ConstructorException("Invalid color: " ~ value, start, end);
throw new Exception("Invalid color: " ~ value);
}
//We don't need to check for uppercase chars this way.
value = value.toLower();
@ -26,7 +26,7 @@ Color constructColorScalar(Mark start, Mark end, ref Node node)
{
if(!std.ascii.isHexDigit(c))
{
throw new ConstructorException("Invalid color: " ~ value, start, end);
throw new Exception("Invalid color: " ~ value);
}
if(std.ascii.isDigit(c))
@ -44,21 +44,16 @@ Color constructColorScalar(Mark start, Mark end, ref Node node)
return result;
}
Color constructColorMapping(Mark start, Mark end, ref Node node)
Color constructColorMapping(ref Node node)
{
ubyte r,g,b;
//Might throw if a value is missing is not an integer, or is out of range.
try
{
r = node["r"].as!ubyte;
g = node["g"].as!ubyte;
b = node["b"].as!ubyte;
}
catch(NodeException e)
{
throw new ConstructorException("Invalid color: " ~ e.msg, start, end);
}
//If this happens, D:YAML will handle the exception and use its message
//in a YAMLException thrown when loading.
r = node["r"].as!ubyte;
g = node["g"].as!ubyte;
b = node["b"].as!ubyte;
return Color(cast(ubyte)r, cast(ubyte)g, cast(ubyte)b);
}

View file

@ -337,17 +337,9 @@ struct TestStruct
}
///Constructor function for TestClass.
TestClass constructClass(Mark start, Mark end, ref Node node)
TestClass constructClass(ref Node node)
{
try
{
return new TestClass(node["x"].as!int, node["y"].as!int, node["z"].as!int);
}
catch(NodeException e)
{
throw new ConstructorException("Error constructing TestClass (missing data members?) "
~ e.msg, start, end);
}
return new TestClass(node["x"].as!int, node["y"].as!int, node["z"].as!int);
}
Node representClass(ref Node node, Representer representer)
@ -362,7 +354,7 @@ Node representClass(ref Node node, Representer representer)
}
///Constructor function for TestStruct.
TestStruct constructStruct(Mark start, Mark end, ref Node node)
TestStruct constructStruct(ref Node node)
{
return TestStruct(to!int(node.as!string));
}