Implement more of the deserializer.

This commit is contained in:
Antoine Aubry 2013-05-10 11:05:22 +01:00
Родитель a7b83ea9fb
Коммит 56e58bed1d
3 изменённых файлов: 333 добавлений и 30 удалений

Просмотреть файл

@ -13,7 +13,7 @@
<tags>yaml parser development library</tags> <tags>yaml parser development library</tags>
</metadata> </metadata>
<files> <files>
<file src="..\YamlDotNet.Core\bin\Release\YamlDotNet.Core.dll" target="lib" /> <file src="..\YamlDotNet.Core\bin\Release\YamlDotNet.Core.dll" target="lib\net35" />
<file src="..\YamlDotNet.Core\bin\Release\YamlDotNet.Core.pdb" target="lib" /> <file src="..\YamlDotNet.Core\bin\Release\YamlDotNet.Core.pdb" target="lib\net35" />
</files> </files>
</package> </package>

Просмотреть файл

@ -78,20 +78,47 @@ namespace YamlDotNet.RepresentationModel.Serialization
/// </summary> /// </summary>
public class Deserializer public class Deserializer
{ {
private readonly ICollection<INodeDeserializer> _deserializers; private readonly IEnumerable<INodeDeserializer> _deserializers;
private readonly IEnumerable<INodeTypeResolver> _typeResolvers;
public Deserializer() public Deserializer()
{ {
// TODO: Allow to override the object factory
var objectFactory = new DefaultObjectFactory();
_deserializers = new INodeDeserializer[] _deserializers = new INodeDeserializer[]
{ {
new NullNodeDeserializer(), new NullNodeDeserializer(),
new ScalarNodeDeserializer(), new ScalarNodeDeserializer(),
new GenericDictionaryDeserializer(objectFactory),
new NonGenericDictionaryDeserializer(objectFactory),
new GenericCollectionDeserializer(objectFactory),
new NonGenericListDeserializer(objectFactory),
new EnumerableDeserializer(),
new ObjectNodeDeserializer(objectFactory),
};
_typeResolvers = new INodeTypeResolver[]
{
new PredefinedTagsNodeTypeResolver(),
new TypeNameInTagNodeTypeResolver(),
new DefaultContainersNodeTypeResolver(),
}; };
} }
public object Deserialize(TextReader input, Type type) public object Deserialize(TextReader input, DeserializationFlags options = DeserializationFlags.None)
{ {
return Deserialize(new EventReader(new Parser(input)), type); return Deserialize(input, typeof(object), options);
}
public object Deserialize(TextReader input, Type type, DeserializationFlags options = DeserializationFlags.None)
{
return Deserialize(new EventReader(new Parser(input)), type, options);
}
public object Deserialize(EventReader reader, DeserializationFlags options = DeserializationFlags.None)
{
return Deserialize(reader, typeof(object), options);
} }
/// <summary> /// <summary>
@ -136,12 +163,12 @@ namespace YamlDotNet.RepresentationModel.Serialization
{ {
var nodeEvent = reader.Peek<NodeEvent>(); var nodeEvent = reader.Peek<NodeEvent>();
var nodeType = GetTypeFromTag(nodeEvent.Tag, expectedType); var nodeType = GetTypeFromEvent(nodeEvent, expectedType);
foreach (var deserializer in _deserializers) foreach (var deserializer in _deserializers)
{ {
object value; object value;
if (deserializer.Deserialize(reader, nodeType, out value)) if (deserializer.Deserialize(reader, nodeType, (r, t) => DeserializeValue(r, t, context), out value))
{ {
return value; return value;
} }
@ -189,6 +216,36 @@ namespace YamlDotNet.RepresentationModel.Serialization
// return false; // return false;
//} //}
private Type GetTypeFromEvent(NodeEvent nodeEvent, Type currentType)//, TagMappings mappings)
{
foreach (var typeResolver in _typeResolvers)
{
if (typeResolver.Resolve(nodeEvent, ref currentType))
{
break;
}
}
return currentType;
}
}
public interface INodeTypeResolver
{
/// <summary>
/// Determines the type of the specified node.
/// </summary>
/// <param name="nodeEvent">The node to be deserialized.</param>
/// <param name="currentType">The type that has been determined so far.</param>
/// <returns>
/// true if <paramref name="currentType"/> has been resolved completely;
/// false if the next type <see cref="INodeTypeResolver"/> should be invoked.
/// </returns>
bool Resolve(NodeEvent nodeEvent, ref Type currentType);
}
public sealed class PredefinedTagsNodeTypeResolver : INodeTypeResolver
{
private static readonly Dictionary<string, Type> predefinedTypes = new Dictionary<string, Type> private static readonly Dictionary<string, Type> predefinedTypes = new Dictionary<string, Type>
{ {
{ "tag:yaml.org,2002:map", typeof(Dictionary<object, object>) }, { "tag:yaml.org,2002:map", typeof(Dictionary<object, object>) },
@ -199,31 +256,61 @@ namespace YamlDotNet.RepresentationModel.Serialization
{ "tag:yaml.org,2002:timestamp", typeof(DateTime) }, { "tag:yaml.org,2002:timestamp", typeof(DateTime) },
}; };
private static Type GetTypeFromTag(string tag, Type defaultType)//, TagMappings mappings) bool INodeTypeResolver.Resolve(NodeEvent nodeEvent, ref Type currentType)
{ {
if (tag == null) Type predefinedType;
if (!string.IsNullOrEmpty(nodeEvent.Tag) && predefinedTypes.TryGetValue(nodeEvent.Tag, out predefinedType))
{ {
return defaultType; currentType = predefinedType;
return true;
}
return false;
}
}
public sealed class DefaultContainersNodeTypeResolver : INodeTypeResolver
{
bool INodeTypeResolver.Resolve(NodeEvent nodeEvent, ref Type currentType)
{
if (currentType == typeof(object))
{
if (nodeEvent is SequenceStart)
{
currentType = typeof(List<object>);
return true;
}
if (nodeEvent is MappingStart)
{
currentType = typeof(Dictionary<object, object>);
return true;
}
} }
//Type predefinedType = mappings.GetMapping(tag); return false;
//if (predefinedType != null || predefinedTypes.TryGetValue(tag, out predefinedType)) }
//{ }
// return predefinedType;
//}
return Type.GetType(tag.Substring(1), true); public sealed class TypeNameInTagNodeTypeResolver : INodeTypeResolver
{
bool INodeTypeResolver.Resolve(NodeEvent nodeEvent, ref Type currentType)
{
if (!string.IsNullOrEmpty(nodeEvent.Tag))
{
currentType = Type.GetType(nodeEvent.Tag.Substring(1), true);
return true;
}
return false;
} }
} }
public interface INodeDeserializer public interface INodeDeserializer
{ {
bool Deserialize(EventReader reader, Type expectedType, out object value); bool Deserialize(EventReader reader, Type expectedType, Func<EventReader, Type, object> nestedObjectDeserializer, out object value);
} }
public sealed class NullNodeDeserializer : INodeDeserializer public sealed class NullNodeDeserializer : INodeDeserializer
{ {
bool INodeDeserializer.Deserialize(EventReader reader, Type expectedType, out object value) bool INodeDeserializer.Deserialize(EventReader reader, Type expectedType, Func<EventReader, Type, object> nestedObjectDeserializer, out object value)
{ {
value = null; value = null;
var evt = reader.Peek<NodeEvent>(); var evt = reader.Peek<NodeEvent>();
@ -240,7 +327,7 @@ namespace YamlDotNet.RepresentationModel.Serialization
public sealed class JsonNullNodeDeserializer : INodeDeserializer public sealed class JsonNullNodeDeserializer : INodeDeserializer
{ {
bool INodeDeserializer.Deserialize(EventReader reader, Type expectedType, out object value) bool INodeDeserializer.Deserialize(EventReader reader, Type expectedType, Func<EventReader, Type, object> nestedObjectDeserializer, out object value)
{ {
value = null; value = null;
var scalar = reader.Peek<Scalar>(); var scalar = reader.Peek<Scalar>();
@ -258,7 +345,7 @@ namespace YamlDotNet.RepresentationModel.Serialization
public sealed class ScalarNodeDeserializer : INodeDeserializer public sealed class ScalarNodeDeserializer : INodeDeserializer
{ {
bool INodeDeserializer.Deserialize(EventReader reader, Type expectedType, out object value) bool INodeDeserializer.Deserialize(EventReader reader, Type expectedType, Func<EventReader, Type, object> nestedObjectDeserializer, out object value)
{ {
var scalar = reader.Allow<Scalar>(); var scalar = reader.Allow<Scalar>();
if (scalar == null) if (scalar == null)
@ -374,4 +461,220 @@ namespace YamlDotNet.RepresentationModel.Serialization
NumberDecimalDigits = 99 NumberDecimalDigits = 99
}; };
} }
public sealed class ObjectNodeDeserializer : INodeDeserializer
{
private readonly IObjectFactory _objectFactory;
public ObjectNodeDeserializer(IObjectFactory objectFactory)
{
_objectFactory = objectFactory;
}
bool INodeDeserializer.Deserialize(EventReader reader, Type expectedType, Func<EventReader, Type, object> nestedObjectDeserializer, out object value)
{
var mapping = reader.Allow<MappingStart>();
if (mapping == null)
{
value = null;
return false;
}
value = _objectFactory.Create(expectedType);
while (!reader.Accept<MappingEnd>())
{
var propertyName = reader.Expect<Scalar>();
// TODO: Find property according to naming conventions
var property = expectedType.GetProperty(propertyName.Value, BindingFlags.Instance | BindingFlags.Public);
var propertyValue = nestedObjectDeserializer(reader, property.PropertyType);
property.SetValue(value, propertyValue, null);
}
reader.Expect<MappingEnd>();
return true;
}
}
public sealed class GenericDictionaryDeserializer : INodeDeserializer
{
private readonly IObjectFactory _objectFactory;
public GenericDictionaryDeserializer(IObjectFactory objectFactory)
{
_objectFactory = objectFactory;
}
bool INodeDeserializer.Deserialize(EventReader reader, Type expectedType, Func<EventReader, Type, object> nestedObjectDeserializer, out object value)
{
var iDictionary = ReflectionUtility.GetImplementedGenericInterface(expectedType, typeof(IDictionary<,>));
if (iDictionary == null)
{
value = false;
return false;
}
reader.Expect<MappingStart>();
value = _objectFactory.Create(expectedType);
_deserializeHelperMethod
.MakeGenericMethod(iDictionary.GetGenericArguments())
.Invoke(null, new object[] { reader, expectedType, nestedObjectDeserializer, value });
reader.Expect<MappingEnd>();
return true;
}
private static MethodInfo _deserializeHelperMethod = typeof(GenericDictionaryDeserializer)
.GetMethod("DeserializeHelper", BindingFlags.Static | BindingFlags.NonPublic);
private static void DeserializeHelper<TKey, TValue>(EventReader reader, Type expectedType, Func<EventReader, Type, object> nestedObjectDeserializer, IDictionary<TKey, TValue> result)
{
while (!reader.Accept<MappingEnd>())
{
var key = (TKey)nestedObjectDeserializer(reader, typeof(TKey));
var value = (TValue)nestedObjectDeserializer(reader, typeof(TValue));
result.Add(key, value);
}
}
}
public sealed class NonGenericDictionaryDeserializer : INodeDeserializer
{
private readonly IObjectFactory _objectFactory;
public NonGenericDictionaryDeserializer(IObjectFactory objectFactory)
{
_objectFactory = objectFactory;
}
bool INodeDeserializer.Deserialize(EventReader reader, Type expectedType, Func<EventReader, Type, object> nestedObjectDeserializer, out object value)
{
if(!typeof(IDictionary).IsAssignableFrom(expectedType))
{
value = false;
return false;
}
reader.Expect<MappingStart>();
var dictionary = (IDictionary)_objectFactory.Create(expectedType);
while (!reader.Accept<MappingEnd>())
{
var key = nestedObjectDeserializer(reader, typeof(object));
var keyValue = nestedObjectDeserializer(reader, typeof(object));
dictionary.Add(key, keyValue);
}
value = dictionary;
reader.Expect<MappingEnd>();
return true;
}
}
public sealed class GenericCollectionDeserializer : INodeDeserializer
{
private readonly IObjectFactory _objectFactory;
public GenericCollectionDeserializer(IObjectFactory objectFactory)
{
_objectFactory = objectFactory;
}
bool INodeDeserializer.Deserialize(EventReader reader, Type expectedType, Func<EventReader, Type, object> nestedObjectDeserializer, out object value)
{
var iCollection = ReflectionUtility.GetImplementedGenericInterface(expectedType, typeof(ICollection<>));
if (iCollection == null)
{
value = false;
return false;
}
reader.Expect<SequenceStart>();
value = _objectFactory.Create(expectedType);
_deserializeHelperMethod
.MakeGenericMethod(iCollection.GetGenericArguments())
.Invoke(null, new object[] { reader, expectedType, nestedObjectDeserializer, value });
reader.Expect<SequenceEnd>();
return true;
}
private static MethodInfo _deserializeHelperMethod = typeof(GenericCollectionDeserializer)
.GetMethod("DeserializeHelper", BindingFlags.Static | BindingFlags.NonPublic);
private static void DeserializeHelper<TItem>(EventReader reader, Type expectedType, Func<EventReader, Type, object> nestedObjectDeserializer, ICollection<TItem> result)
{
while (!reader.Accept<SequenceEnd>())
{
var value = (TItem)nestedObjectDeserializer(reader, typeof(TItem));
result.Add(value);
}
}
}
public sealed class NonGenericListDeserializer : INodeDeserializer
{
private readonly IObjectFactory _objectFactory;
public NonGenericListDeserializer(IObjectFactory objectFactory)
{
_objectFactory = objectFactory;
}
bool INodeDeserializer.Deserialize(EventReader reader, Type expectedType, Func<EventReader, Type, object> nestedObjectDeserializer, out object value)
{
if (!typeof(IList).IsAssignableFrom(expectedType))
{
value = false;
return false;
}
reader.Expect<SequenceStart>();
var list = (IList)_objectFactory.Create(expectedType);
while (!reader.Accept<SequenceEnd>())
{
var item = nestedObjectDeserializer(reader, typeof(object));
list.Add(item);
}
value = list;
reader.Expect<SequenceEnd>();
return true;
}
}
public sealed class EnumerableDeserializer : INodeDeserializer
{
bool INodeDeserializer.Deserialize(EventReader reader, Type expectedType, Func<EventReader, Type, object> nestedObjectDeserializer, out object value)
{
Type itemsType;
if (expectedType == typeof(IEnumerable))
{
itemsType = typeof(object);
}
else
{
var iEnumerable = ReflectionUtility.GetImplementedGenericInterface(expectedType, typeof(IEnumerable<>));
if (iEnumerable != expectedType)
{
value = null;
return false;
}
itemsType = iEnumerable.GetGenericArguments()[0];
}
var collectionType = typeof(List<>).MakeGenericType(itemsType);
value = nestedObjectDeserializer(reader, collectionType);
return true;
}
}
} }

Просмотреть файл

@ -310,7 +310,7 @@ namespace YamlDotNet.UnitTests.RepresentationModel
[Fact] [Fact]
public void DeserializeDictionary() public void DeserializeDictionary()
{ {
YamlSerializer serializer = new YamlSerializer(); var serializer = new Deserializer();
object result = serializer.Deserialize(YamlFile("dictionary.yaml")); object result = serializer.Deserialize(YamlFile("dictionary.yaml"));
Assert.True(typeof(IDictionary<object, object>).IsAssignableFrom(result.GetType()), "The deserialized object has the wrong type."); Assert.True(typeof(IDictionary<object, object>).IsAssignableFrom(result.GetType()), "The deserialized object has the wrong type.");
@ -323,7 +323,7 @@ namespace YamlDotNet.UnitTests.RepresentationModel
[Fact] [Fact]
public void DeserializeExplicitDictionary() public void DeserializeExplicitDictionary()
{ {
YamlSerializer serializer = new YamlSerializer(); var serializer = new Deserializer();
object result = serializer.Deserialize(YamlFile("dictionaryExplicit.yaml")); object result = serializer.Deserialize(YamlFile("dictionaryExplicit.yaml"));
Assert.True(typeof(IDictionary<string, int>).IsAssignableFrom(result.GetType()), "The deserialized object has the wrong type."); Assert.True(typeof(IDictionary<string, int>).IsAssignableFrom(result.GetType()), "The deserialized object has the wrong type.");
@ -336,8 +336,8 @@ namespace YamlDotNet.UnitTests.RepresentationModel
[Fact] [Fact]
public void DeserializeListOfDictionaries() public void DeserializeListOfDictionaries()
{ {
var serializer = new YamlSerializer<List<Dictionary<string, string>>>(); var serializer = new Deserializer();
object result = serializer.Deserialize(YamlFile("listOfDictionaries.yaml")); object result = serializer.Deserialize(YamlFile("listOfDictionaries.yaml"), typeof(List<Dictionary<string, string>>));
Assert.IsType<List<Dictionary<string, string>>>(result); Assert.IsType<List<Dictionary<string, string>>>(result);
@ -351,7 +351,7 @@ namespace YamlDotNet.UnitTests.RepresentationModel
[Fact] [Fact]
public void DeserializeList() public void DeserializeList()
{ {
YamlSerializer serializer = new YamlSerializer(); var serializer = new Deserializer();
object result = serializer.Deserialize(YamlFile("list.yaml")); object result = serializer.Deserialize(YamlFile("list.yaml"));
Assert.True(typeof(IList).IsAssignableFrom(result.GetType())); Assert.True(typeof(IList).IsAssignableFrom(result.GetType()));
@ -365,7 +365,7 @@ namespace YamlDotNet.UnitTests.RepresentationModel
[Fact] [Fact]
public void DeserializeExplicitList() public void DeserializeExplicitList()
{ {
YamlSerializer serializer = new YamlSerializer(); var serializer = new Deserializer();
object result = serializer.Deserialize(YamlFile("listExplicit.yaml")); object result = serializer.Deserialize(YamlFile("listExplicit.yaml"));
Assert.True(typeof(IList<int>).IsAssignableFrom(result.GetType())); Assert.True(typeof(IList<int>).IsAssignableFrom(result.GetType()));
@ -384,8 +384,8 @@ namespace YamlDotNet.UnitTests.RepresentationModel
StringWriter buffer = new StringWriter(); StringWriter buffer = new StringWriter();
serializer.Serialize(buffer, z); serializer.Serialize(buffer, z);
YamlSerializer<IEnumerable<Z>> deserializer = new YamlSerializer<IEnumerable<Z>>(); var deserializer = new Deserializer();
IEnumerable<Z> result = deserializer.Deserialize(new StringReader(buffer.ToString())); var result = (IEnumerable<Z>)deserializer.Deserialize(new StringReader(buffer.ToString()), typeof(IEnumerable<Z>));
Assert.Equal(1, result.Count()); Assert.Equal(1, result.Count());
Assert.Equal("Yo", result.First().aaa); Assert.Equal("Yo", result.First().aaa);
} }
@ -437,7 +437,7 @@ namespace YamlDotNet.UnitTests.RepresentationModel
DeserializationOptions options = new DeserializationOptions(); DeserializationOptions options = new DeserializationOptions();
options.Overrides.Add(typeof(Z), "aaa", (t, reader) => ((Z)t).aaa = reader.Expect<Scalar>().Value.ToUpperInvariant()); options.Overrides.Add(typeof(Z), "aaa", (t, reader) => ((Z)t).aaa = reader.Expect<Scalar>().Value.ToUpperInvariant());
YamlSerializer serializer = new YamlSerializer(); var serializer = new YamlSerializer();
object result = serializer.Deserialize(YamlFile("explicitType.yaml"), options); object result = serializer.Deserialize(YamlFile("explicitType.yaml"), options);
Assert.True(typeof(Z).IsAssignableFrom(result.GetType())); Assert.True(typeof(Z).IsAssignableFrom(result.GetType()));
@ -466,7 +466,7 @@ namespace YamlDotNet.UnitTests.RepresentationModel
DeserializationOptions options = new DeserializationOptions(); DeserializationOptions options = new DeserializationOptions();
options.Mappings.Add("tag:yaml.org,2002:point", typeof(Point)); options.Mappings.Add("tag:yaml.org,2002:point", typeof(Point));
YamlSerializer serializer = new YamlSerializer(); var serializer = new YamlSerializer();
object result = serializer.Deserialize(YamlFile("tags.yaml"), options); object result = serializer.Deserialize(YamlFile("tags.yaml"), options);
Assert.Equal(typeof(Point), result.GetType()); Assert.Equal(typeof(Point), result.GetType());