зеркало из https://github.com/stride3d/SharpYaml.git
Allow to customize the creation of object during deserialization.
This commit is contained in:
Родитель
8d130890fd
Коммит
712bc5537b
|
@ -0,0 +1,41 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace YamlDotNet.RepresentationModel.Serialization
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates objects using <see cref="Activator.CreateInstance"/>.
|
||||
/// </summary>
|
||||
public sealed class DefaultObjectFactory : IObjectFactory
|
||||
{
|
||||
private static readonly Dictionary<Type, Type> defaultInterfaceImplementations = new Dictionary<Type, Type>
|
||||
{
|
||||
{ typeof(IEnumerable<>), typeof(List<>) },
|
||||
{ typeof(ICollection<>), typeof(List<>) },
|
||||
{ typeof(IList<>), typeof(List<>) },
|
||||
{ typeof(IDictionary<,>), typeof(Dictionary<,>) },
|
||||
};
|
||||
|
||||
public object Create(Type type)
|
||||
{
|
||||
if (type.IsInterface)
|
||||
{
|
||||
Type implementationType;
|
||||
if (defaultInterfaceImplementations.TryGetValue(type.GetGenericTypeDefinition(), out implementationType))
|
||||
{
|
||||
type = implementationType.MakeGenericType(type.GetGenericArguments());
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return Activator.CreateInstance(type);
|
||||
}
|
||||
catch (MissingMethodException err)
|
||||
{
|
||||
var message = string.Format("Failed to create an instance of type '{0}'.", type);
|
||||
throw new InvalidOperationException(message, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -59,6 +59,27 @@ namespace YamlDotNet.RepresentationModel.Serialization
|
|||
}
|
||||
}
|
||||
|
||||
private IObjectFactory objectFactory = new DefaultObjectFactory();
|
||||
|
||||
/// <summary>
|
||||
/// Gets / sets the <see cref="IObjectFactory"/> that is used to create instances of objects when deserializing.
|
||||
/// </summary>
|
||||
public IObjectFactory ObjectFactory
|
||||
{
|
||||
get
|
||||
{
|
||||
return objectFactory;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException("ObjectFactory");
|
||||
}
|
||||
objectFactory = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DeserializationOptions"/> class.
|
||||
/// </summary>
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
using System;
|
||||
|
||||
namespace YamlDotNet.RepresentationModel.Serialization
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates instances of types.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This interface allows to provide a custom logic for creating instances during deserialization.
|
||||
/// </remarks>
|
||||
public interface IObjectFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an instance of the specified type.
|
||||
/// </summary>
|
||||
object Create(Type type);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
|
||||
namespace YamlDotNet.RepresentationModel.Serialization
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates objects using a <see cref="Func{Type,object}"/>.
|
||||
/// </summary>
|
||||
public sealed class LambdaObjectFactory : IObjectFactory
|
||||
{
|
||||
private readonly Func<Type, object> _factory;
|
||||
|
||||
public LambdaObjectFactory(Func<Type, object> factory)
|
||||
{
|
||||
if (factory == null)
|
||||
{
|
||||
throw new ArgumentNullException("factory");
|
||||
}
|
||||
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
public object Create(Type type)
|
||||
{
|
||||
return _factory(type);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -34,7 +34,7 @@ using YamlDotNet.Core.Events;
|
|||
|
||||
namespace YamlDotNet.RepresentationModel.Serialization
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reads and writes objects from and to YAML.
|
||||
/// </summary>
|
||||
|
@ -350,7 +350,7 @@ namespace YamlDotNet.RepresentationModel.Serialization
|
|||
|
||||
private object DeserializeValueNotNull(EventReader reader, DeserializationContext context, INodeEvent nodeEvent, Type expectedType)
|
||||
{
|
||||
Type type = GetType(nodeEvent.Tag, expectedType, context.Options.Mappings);
|
||||
Type type = GetTypeFromTag(nodeEvent.Tag, expectedType, context.Options.Mappings);
|
||||
|
||||
var converter = converters.FirstOrDefault(c => c.Accepts(type));
|
||||
if (converter != null)
|
||||
|
@ -360,7 +360,7 @@ namespace YamlDotNet.RepresentationModel.Serialization
|
|||
|
||||
if (typeof(IYamlSerializable).IsAssignableFrom(type))
|
||||
{
|
||||
return DeserializeYamlSerializable(reader, type);
|
||||
return DeserializeYamlSerializable(reader, type, context);
|
||||
}
|
||||
|
||||
if (reader.Accept<MappingStart>())
|
||||
|
@ -386,7 +386,7 @@ namespace YamlDotNet.RepresentationModel.Serialization
|
|||
Scalar scalar = reader.Expect<Scalar>();
|
||||
|
||||
object result;
|
||||
type = GetType(scalar.Tag, type, context.Options.Mappings);
|
||||
type = GetTypeFromTag(scalar.Tag, type, context.Options.Mappings);
|
||||
|
||||
if (type.IsEnum)
|
||||
{
|
||||
|
@ -521,7 +521,7 @@ namespace YamlDotNet.RepresentationModel.Serialization
|
|||
{
|
||||
SequenceStart sequence = reader.Expect<SequenceStart>();
|
||||
|
||||
type = GetType(sequence.Tag, type, context.Options.Mappings);
|
||||
type = GetTypeFromTag(sequence.Tag, type, context.Options.Mappings);
|
||||
|
||||
// Choose a default list type in case there was no specific type specified.
|
||||
if (type == typeof(object))
|
||||
|
@ -529,34 +529,31 @@ namespace YamlDotNet.RepresentationModel.Serialization
|
|||
type = typeof(ArrayList);
|
||||
}
|
||||
|
||||
object result;
|
||||
object result;
|
||||
|
||||
Type iCollection = ReflectionUtility.GetImplementedGenericInterface(type, typeof(ICollection<>));
|
||||
if (iCollection != null) // Generic list
|
||||
{
|
||||
Type[] iCollectionArguments = iCollection.GetGenericArguments();
|
||||
Debug.Assert(iCollectionArguments.Length == 1, "ICollection<> must have one generic argument.");
|
||||
var itemType = iCollectionArguments[0];
|
||||
var itemType = iCollectionArguments[0];
|
||||
|
||||
//result = type.IsArray ? typeof(List<>).MakeGenericType(iCollectionArguments) : Activator.CreateInstance(type);
|
||||
//MethodInfo addAdapter = addAdapterGeneric.MakeGenericMethod(iCollectionArguments);
|
||||
//Action<object, object> addAdapterDelegate = (Action<object, object>)Delegate.CreateDelegate(typeof(Action<object, object>), addAdapter);
|
||||
if (type.IsArray)
|
||||
{
|
||||
var tempListType = typeof(List<>).MakeGenericType(iCollectionArguments);
|
||||
var tempList = Activator.CreateInstance(tempListType);
|
||||
DeserializeGenericListInternal(reader, tempList, itemType, context);
|
||||
result = tempListType.GetMethod("ToArray", Type.EmptyTypes).Invoke(tempList, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = Activator.CreateInstance(type);
|
||||
DeserializeGenericListInternal(reader, result, itemType, context);
|
||||
}
|
||||
if (type.IsArray)
|
||||
{
|
||||
var tempListType = typeof(List<>).MakeGenericType(iCollectionArguments);
|
||||
var tempList = Activator.CreateInstance(tempListType);
|
||||
DeserializeGenericListInternal(reader, tempList, itemType, context);
|
||||
result = tempListType.GetMethod("ToArray", Type.EmptyTypes).Invoke(tempList, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = context.Options.ObjectFactory.Create(type);
|
||||
DeserializeGenericListInternal(reader, result, itemType, context);
|
||||
}
|
||||
}
|
||||
else // Non-generic list
|
||||
{
|
||||
result = Activator.CreateInstance(type);
|
||||
result = context.Options.ObjectFactory.Create(type);
|
||||
var list = result as IList;
|
||||
if (list != null)
|
||||
{
|
||||
|
@ -573,10 +570,10 @@ namespace YamlDotNet.RepresentationModel.Serialization
|
|||
return result;
|
||||
}
|
||||
|
||||
private void DeserializeGenericListInternal(EventReader reader, object list, Type itemType, DeserializationContext context)
|
||||
private void DeserializeGenericListInternal(EventReader reader, object list, Type itemType, DeserializationContext context)
|
||||
{
|
||||
var addAdapter = addAdapterGeneric.MakeGenericMethod(new Type[] { itemType });
|
||||
var addAdapterDelegate = (Action<object, object>)Delegate.CreateDelegate(typeof(Action<object, object>), addAdapter);
|
||||
var addAdapter = addAdapterGeneric.MakeGenericMethod(new Type[] { itemType });
|
||||
var addAdapterDelegate = (Action<object, object>)Delegate.CreateDelegate(typeof(Action<object, object>), addAdapter);
|
||||
while (!reader.Accept<SequenceEnd>())
|
||||
{
|
||||
addAdapterDelegate(list, DeserializeValue(reader, itemType, context));
|
||||
|
@ -600,9 +597,9 @@ namespace YamlDotNet.RepresentationModel.Serialization
|
|||
return converter.ReadYaml(reader.Parser, type);
|
||||
}
|
||||
|
||||
private static object DeserializeYamlSerializable(EventReader reader, Type type)
|
||||
private static object DeserializeYamlSerializable(EventReader reader, Type type, DeserializationContext context)
|
||||
{
|
||||
IYamlSerializable result = (IYamlSerializable)Activator.CreateInstance(type);
|
||||
IYamlSerializable result = (IYamlSerializable)context.Options.ObjectFactory.Create(type);
|
||||
result.ReadYaml(reader.Parser);
|
||||
return result;
|
||||
}
|
||||
|
@ -611,17 +608,8 @@ namespace YamlDotNet.RepresentationModel.Serialization
|
|||
{
|
||||
MappingStart mapping = reader.Expect<MappingStart>();
|
||||
|
||||
type = GetType(mapping.Tag, type, context.Options.Mappings);
|
||||
object result;
|
||||
try
|
||||
{
|
||||
result = Activator.CreateInstance(type);
|
||||
}
|
||||
catch (MissingMethodException err)
|
||||
{
|
||||
var message = string.Format("Failed to create an instance of type '{0}'.", type);
|
||||
throw new InvalidOperationException(message, err);
|
||||
}
|
||||
type = GetTypeFromTag(mapping.Tag, type, context.Options.Mappings);
|
||||
var result = context.Options.ObjectFactory.Create(type);
|
||||
|
||||
IDictionary dictionary = result as IDictionary;
|
||||
if (dictionary != null)
|
||||
|
@ -702,30 +690,6 @@ namespace YamlDotNet.RepresentationModel.Serialization
|
|||
{ "tag:yaml.org,2002:timestamp", typeof(DateTime) },
|
||||
};
|
||||
|
||||
private static readonly Dictionary<Type, Type> defaultInterfaceImplementations = new Dictionary<Type, Type>
|
||||
{
|
||||
{ typeof(IEnumerable<>), typeof(List<>) },
|
||||
{ typeof(ICollection<>), typeof(List<>) },
|
||||
{ typeof(IList<>), typeof(List<>) },
|
||||
{ typeof(IDictionary<,>), typeof(Dictionary<,>) },
|
||||
};
|
||||
|
||||
private static Type GetType(string tag, Type defaultType, TagMappings mappings)
|
||||
{
|
||||
Type actualType = GetTypeFromTag(tag, defaultType, mappings);
|
||||
|
||||
if (actualType.IsInterface)
|
||||
{
|
||||
Type implementationType;
|
||||
if (defaultInterfaceImplementations.TryGetValue(actualType.GetGenericTypeDefinition(), out implementationType))
|
||||
{
|
||||
return implementationType.MakeGenericType(actualType.GetGenericArguments());
|
||||
}
|
||||
}
|
||||
|
||||
return actualType;
|
||||
}
|
||||
|
||||
private static Type GetTypeFromTag(string tag, Type defaultType, TagMappings mappings)
|
||||
{
|
||||
if (tag == null)
|
||||
|
|
|
@ -81,12 +81,15 @@
|
|||
<Compile Include="Serialization\ChainedObjectGraphVisitor.cs" />
|
||||
<Compile Include="Serialization\CustomSerializationObjectGraphVisitor.cs" />
|
||||
<Compile Include="Serialization\DefaultExclusiveObjectGraphVisitor.cs" />
|
||||
<Compile Include="Serialization\DefaultObjectFactory.cs" />
|
||||
<Compile Include="Serialization\DeserializationContext.cs" />
|
||||
<Compile Include="Serialization\DeserializationOptions.cs" />
|
||||
<Compile Include="Serialization\DeserializationOverride.cs" />
|
||||
<Compile Include="Serialization\DeserializationOverrides.cs" />
|
||||
<None Include="Serialization\Deserializer.cs" />
|
||||
<Compile Include="Serialization\EmittingObjectGraphVisitor.cs" />
|
||||
<Compile Include="Serialization\IObjectFactory.cs" />
|
||||
<Compile Include="Serialization\LambdaObjectFactory.cs" />
|
||||
<Compile Include="Serialization\Serializer.cs" />
|
||||
<Compile Include="Serialization\WriterEventEmitter.cs" />
|
||||
<Compile Include="Serialization\EventInfo.cs" />
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Xunit;
|
||||
using YamlDotNet.RepresentationModel.Serialization;
|
||||
|
||||
namespace YamlDotNet.UnitTests.RepresentationModel
|
||||
{
|
||||
public class ObjectFactoryTests
|
||||
{
|
||||
public class FooBase
|
||||
{
|
||||
}
|
||||
|
||||
public class FooDerived : FooBase
|
||||
{
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NotSpecifyingObjectFactoryUsesDefault()
|
||||
{
|
||||
var serializer = new YamlSerializer();
|
||||
var options = new DeserializationOptions();
|
||||
options.Mappings.Add("!foo", typeof(FooBase));
|
||||
var result = serializer.Deserialize(new StringReader("!foo {}"), options);
|
||||
|
||||
Assert.IsType<FooBase>(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ObjectFactoryIsInvoked()
|
||||
{
|
||||
var serializer = new YamlSerializer();
|
||||
var options = new DeserializationOptions();
|
||||
options.Mappings.Add("!foo", typeof(FooBase));
|
||||
|
||||
options.ObjectFactory = new LambdaObjectFactory(t => new FooDerived());
|
||||
|
||||
var result = serializer.Deserialize(new StringReader("!foo {}"), options);
|
||||
|
||||
Assert.IsType<FooDerived>(result);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -57,6 +57,7 @@
|
|||
<Compile Include="Core\ScannerTests.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="RepresentationModel\ObjectConverterTests.cs" />
|
||||
<Compile Include="RepresentationModel\ObjectFactoryTests.cs" />
|
||||
<Compile Include="RepresentationModel\Samples.cs" />
|
||||
<Compile Include="RepresentationModel\SerializationTests.cs" />
|
||||
<Compile Include="RepresentationModel\YamlStreamTests.cs" />
|
||||
|
|
Загрузка…
Ссылка в новой задаче