Skip to content

Commit

Permalink
Merge pull request #186 from aaubry/feat-iyamlconvertible
Browse files Browse the repository at this point in the history
Improve the usability of YamlDocument et al
  • Loading branch information
aaubry authored Jun 17, 2016
2 parents 1459634 + 01b0679 commit 1befe05
Show file tree
Hide file tree
Showing 17 changed files with 481 additions and 23 deletions.
108 changes: 108 additions & 0 deletions YamlDotNet.Test/Serialization/RepresentationModelSerializationTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xunit;
using YamlDotNet.Core;
using YamlDotNet.RepresentationModel;
using YamlDotNet.Serialization;

namespace YamlDotNet.Test.Serialization
{
public class RepresentationModelSerializationTests
{
[Theory]
[InlineData("hello", "hello")]
[InlineData("'hello'", "hello")]
[InlineData("\"hello\"", "hello")]
[InlineData("!!int 123", "123")]
public void ScalarIsSerializable(string yaml, string expectedValue)
{
var deserializer = new Deserializer();
var node = deserializer.Deserialize<YamlScalarNode>(Yaml.ReaderForText(yaml));

Assert.NotNull(node);
Assert.Equal(expectedValue, node.Value);

var serializer = new Serializer();
var buffer = new StringWriter();
serializer.Serialize(buffer, node);
Assert.Equal(yaml, buffer.ToString().TrimEnd('\r', '\n', '.'));
}

[Theory]
[InlineData("[a]", new[] { "a" })]
[InlineData("['a']", new[] { "a" })]
[InlineData("- a\r\n- b", new[] { "a", "b" })]
public void SequenceIsSerializable(string yaml, string[] expectedValues)
{
var deserializer = new Deserializer();
var node = deserializer.Deserialize<YamlSequenceNode>(Yaml.ReaderForText(yaml));

Assert.NotNull(node);
Assert.Equal(expectedValues.Length, node.Children.Count);
for (int i = 0; i < expectedValues.Length; i++)
{
Assert.Equal(expectedValues[i], ((YamlScalarNode)node.Children[i]).Value);
}

var serializer = new Serializer();
var buffer = new StringWriter();
serializer.Serialize(buffer, node);
Assert.Equal(yaml, buffer.ToString().TrimEnd('\r', '\n', '.'));
}

[Theory]
[InlineData("{a: b}", new[] { "a", "b" })]
[InlineData("{'a': \"b\"}", new[] { "a", "b" })]
[InlineData("a: b\r\nc: d", new[] { "a", "b", "c", "d" })]
public void MappingIsSerializable(string yaml, string[] expectedKeysAndValues)
{
var deserializer = new Deserializer();
var node = deserializer.Deserialize<YamlMappingNode>(Yaml.ReaderForText(yaml));

Assert.NotNull(node);
Assert.Equal(expectedKeysAndValues.Length / 2, node.Children.Count);
for (int i = 0; i < expectedKeysAndValues.Length; i += 2)
{
Assert.Equal(expectedKeysAndValues[i + 1], ((YamlScalarNode)node.Children[expectedKeysAndValues[i]]).Value);
}

var serializer = new Serializer();
var buffer = new StringWriter();
serializer.Serialize(buffer, node);
Assert.Equal(yaml, buffer.ToString().TrimEnd('\r', '\n', '.'));
}
}

public class ByteArrayConverter : IYamlTypeConverter
{
public bool Accepts(Type type)
{
return type == typeof(byte[]);
}

public object ReadYaml(IParser parser, Type type)
{
var scalar = (YamlDotNet.Core.Events.Scalar)parser.Current;
var bytes = Convert.FromBase64String(scalar.Value);
parser.MoveNext();
return bytes;
}

public void WriteYaml(IEmitter emitter, object value, Type type)
{
var bytes = (byte[])value;
emitter.Emit(new YamlDotNet.Core.Events.Scalar(
null,
"tag:yaml.org,2002:binary",
Convert.ToBase64String(bytes),
ScalarStyle.Plain,
false,
false
));
}
}
}
1 change: 1 addition & 0 deletions YamlDotNet.Test/YamlDotNet.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
<Compile Include="Serialization\GenericTestList.cs" />
<Compile Include="Serialization\NamingConventionTests.cs" />
<Compile Include="Serialization\ObjectFactoryTests.cs" />
<Compile Include="Serialization\RepresentationModelSerializationTests.cs" />
<Compile Include="Serialization\SerializationTestHelper.cs" />
<Compile Include="Serialization\SerializationTests.cs" />
<Compile Include="Serialization\CodeValidations.cs" />
Expand Down
2 changes: 1 addition & 1 deletion YamlDotNet/RepresentationModel/YamlDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ private void VisitNode(YamlNode node)

protected override void Visit(YamlScalarNode scalar)
{
VisitNode(scalar);
// Do not assign anchors to scalars
}

protected override void Visit(YamlMappingNode mapping)
Expand Down
45 changes: 43 additions & 2 deletions YamlDotNet/RepresentationModel/YamlMappingNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,19 @@

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;

namespace YamlDotNet.RepresentationModel
{
/// <summary>
/// Represents a mapping node in the YAML document.
/// </summary>
[Serializable]
public class YamlMappingNode : YamlNode, IEnumerable<KeyValuePair<YamlNode, YamlNode>>
public class YamlMappingNode : YamlNode, IEnumerable<KeyValuePair<YamlNode, YamlNode>>, IYamlConvertible
{
private readonly IDictionary<YamlNode, YamlNode> children = new Dictionary<YamlNode, YamlNode>();

Expand Down Expand Up @@ -60,9 +62,15 @@ public IDictionary<YamlNode, YamlNode> Children
/// <param name="events">The events.</param>
/// <param name="state">The state.</param>
internal YamlMappingNode(EventReader events, DocumentLoadingState state)
{
Load(events, state);
}

private void Load(EventReader events, DocumentLoadingState state)
{
MappingStart mapping = events.Expect<MappingStart>();
Load(mapping, state);
base.Load(mapping, state);
Style = mapping.Style;

bool hasUnresolvedAliases = false;
while (!events.Accept<MappingEnd>())
Expand Down Expand Up @@ -385,5 +393,38 @@ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
}

#endregion

void IYamlConvertible.Read(EventReader reader, Type expectedType, Func<EventReader, Type, object> nestedObjectDeserializer)
{
Load(reader, new DocumentLoadingState());
}

void IYamlConvertible.Write(IEmitter emitter)
{
Emit(emitter, new EmitterState());
}

/// <summary>
/// Creates a <see cref="YamlMappingNode" /> containing a key-value pair for each property of the specified object.
/// </summary>
public static YamlMappingNode FromObject(object mapping)
{
if (mapping == null)
{
throw new ArgumentNullException("mapping");
}

var result = new YamlMappingNode();
foreach (var property in mapping.GetType().GetPublicProperties())
{
if (property.CanRead && property.GetGetMethod().GetParameters().Length == 0)
{
var value = property.GetValue(mapping, null);
var valueNode = (value as YamlNode) ?? (Convert.ToString(value));
result.Add(property.Name, valueNode);
}
}
return result;
}
}
}
57 changes: 54 additions & 3 deletions YamlDotNet/RepresentationModel/YamlNode.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
// This file is part of YamlDotNet - A .NET library for YAML.
// Copyright (c) Antoine Aubry and contributors

// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
Expand All @@ -20,6 +20,7 @@
// SOFTWARE.

using System;
using System.Linq;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using System.Collections.Generic;
Expand Down Expand Up @@ -212,5 +213,55 @@ public abstract YamlNodeType NodeType
{
get;
}

/// <summary>
/// Performs an implicit conversion from <see cref="System.String"/> to <see cref="YamlDotNet.RepresentationModel.YamlNode"/>.
/// </summary>
/// <param name="value">The value.</param>
/// <returns>The result of the conversion.</returns>
public static implicit operator YamlNode(string value)
{
return new YamlScalarNode(value);
}

/// <summary>
/// Performs an implicit conversion from string[] to <see cref="YamlDotNet.RepresentationModel.YamlNode"/>.
/// </summary>
/// <param name="sequence">The value.</param>
/// <returns>The result of the conversion.</returns>
public static implicit operator YamlNode(string[] sequence)
{
return new YamlSequenceNode(sequence.Select(i => (YamlNode)i));
}

/// <summary>
/// Converts a <see cref="YamlScalarNode" /> to a string by returning its value.
/// </summary>
public static explicit operator string(YamlNode scalar)
{
return ((YamlScalarNode)scalar).Value;
}

/// <summary>
/// Gets the nth element in a <see cref="YamlSequenceNode" />.
/// </summary>
public YamlNode this[int index]
{
get
{
return ((YamlSequenceNode)this).Children[index];
}
}

/// <summary>
/// Gets the value associated with a key in a <see cref="YamlMappingNode" />.
/// </summary>
public YamlNode this[YamlNode key]
{
get
{
return ((YamlMappingNode)this).Children[key];
}
}
}
}
32 changes: 19 additions & 13 deletions YamlDotNet/RepresentationModel/YamlScalarNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
using System.Diagnostics;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;

namespace YamlDotNet.RepresentationModel
{
Expand All @@ -33,7 +34,7 @@ namespace YamlDotNet.RepresentationModel
/// </summary>
[DebuggerDisplay("{Value}")]
[Serializable]
public class YamlScalarNode : YamlNode
public class YamlScalarNode : YamlNode, IYamlConvertible
{
/// <summary>
/// Gets or sets the value of the node.
Expand All @@ -53,9 +54,14 @@ public class YamlScalarNode : YamlNode
/// <param name="events">The events.</param>
/// <param name="state">The state.</param>
internal YamlScalarNode(EventReader events, DocumentLoadingState state)
{
Load(events, state);
}

private void Load(EventReader events, DocumentLoadingState state)
{
Scalar scalar = events.Expect<Scalar>();
Load(scalar, state);
base.Load(scalar, state);
Value = scalar.Value;
Style = scalar.Style;
}
Expand Down Expand Up @@ -92,7 +98,7 @@ internal override void ResolveAliases(DocumentLoadingState state)
/// <param name="state">The state.</param>
internal override void Emit(IEmitter emitter, EmitterState state)
{
emitter.Emit(new Scalar(Anchor, Tag, Value, Style, true, false));
emitter.Emit(new Scalar(Anchor, Tag, Value, Style, Tag == null, false));
}

/// <summary>
Expand Down Expand Up @@ -126,16 +132,6 @@ public override int GetHashCode()
);
}

/// <summary>
/// Performs an implicit conversion from <see cref="System.String"/> to <see cref="YamlDotNet.RepresentationModel.YamlScalarNode"/>.
/// </summary>
/// <param name="value">The value.</param>
/// <returns>The result of the conversion.</returns>
public static implicit operator YamlScalarNode(string value)
{
return new YamlScalarNode(value);
}

/// <summary>
/// Performs an explicit conversion from <see cref="YamlDotNet.RepresentationModel.YamlScalarNode"/> to <see cref="System.String"/>.
/// </summary>
Expand Down Expand Up @@ -172,5 +168,15 @@ public override YamlNodeType NodeType
{
get { return YamlNodeType.Scalar; }
}

void IYamlConvertible.Read(EventReader reader, Type expectedType, Func<EventReader, Type, object> nestedObjectDeserializer)
{
Load(reader, new DocumentLoadingState());
}

void IYamlConvertible.Write(IEmitter emitter)
{
Emit(emitter, new EmitterState());
}
}
}
Loading

0 comments on commit 1befe05

Please sign in to comment.