From 58f598236bf71cfe9f6eec3bcf972b6115f1107c Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Tue, 30 Apr 2013 10:34:35 +0100 Subject: [PATCH] Can roundrip with type converter and no default ctor When using the roundtrip traversal strategy, there's a check that the types involved all have default ctors (otherwise they can't be deserialized). But this check didn't take into account custom type converters, which can deserialize without default ctors. --- .../FullObjectGraphTraversalStrategy.cs | 4 +- .../RoundtripObjectGraphTraversalStrategy.cs | 9 ++-- .../Serialization/Serializer.cs | 15 ++++-- .../RepresentationModel/SerializationTests.cs | 49 ++++++++++++++++--- 4 files changed, 59 insertions(+), 18 deletions(-) diff --git a/YamlDotNet.RepresentationModel/Serialization/FullObjectGraphTraversalStrategy.cs b/YamlDotNet.RepresentationModel/Serialization/FullObjectGraphTraversalStrategy.cs index f9d93f8..eea4e1e 100644 --- a/YamlDotNet.RepresentationModel/Serialization/FullObjectGraphTraversalStrategy.cs +++ b/YamlDotNet.RepresentationModel/Serialization/FullObjectGraphTraversalStrategy.cs @@ -13,15 +13,17 @@ namespace YamlDotNet.RepresentationModel.Serialization /// public class FullObjectGraphTraversalStrategy : IObjectGraphTraversalStrategy { + protected readonly Serializer serializer; private readonly int maxRecursion; - public FullObjectGraphTraversalStrategy(int maxRecursion) + public FullObjectGraphTraversalStrategy(Serializer serializer, int maxRecursion) { if(maxRecursion <= 0) { throw new ArgumentOutOfRangeException("maxRecursion", maxRecursion, "maxRecursion must be greater than 1"); } + this.serializer = serializer; this.maxRecursion = maxRecursion; } diff --git a/YamlDotNet.RepresentationModel/Serialization/RoundtripObjectGraphTraversalStrategy.cs b/YamlDotNet.RepresentationModel/Serialization/RoundtripObjectGraphTraversalStrategy.cs index 6b04b64..d234797 100644 --- a/YamlDotNet.RepresentationModel/Serialization/RoundtripObjectGraphTraversalStrategy.cs +++ b/YamlDotNet.RepresentationModel/Serialization/RoundtripObjectGraphTraversalStrategy.cs @@ -1,5 +1,6 @@ using System; using System.Globalization; +using System.Linq; using System.Reflection; namespace YamlDotNet.RepresentationModel.Serialization @@ -22,16 +23,16 @@ namespace YamlDotNet.RepresentationModel.Serialization // base.TraverseObject(value, type, visitor); //} - public RoundtripObjectGraphTraversalStrategy(int maxRecursion) - : base(maxRecursion) + public RoundtripObjectGraphTraversalStrategy(Serializer serializer, int maxRecursion) + : base(serializer, maxRecursion) { } protected override void SerializeProperties(object value, Type type, IObjectGraphVisitor visitor, int currentDepth) { - if (!ReflectionUtility.HasDefaultConstructor(type)) + if (!ReflectionUtility.HasDefaultConstructor(type) && !serializer.Converters.Any(c => c.Accepts(type))) { - throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Type '{0}' cannot be deserialized because it does not have a default constructor.", type)); + throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Type '{0}' cannot be deserialized because it does not have a default constructor or a type converter.", type)); } base.SerializeProperties(value, type, visitor, currentDepth); diff --git a/YamlDotNet.RepresentationModel/Serialization/Serializer.cs b/YamlDotNet.RepresentationModel/Serialization/Serializer.cs index 89a24e9..9014461 100644 --- a/YamlDotNet.RepresentationModel/Serialization/Serializer.cs +++ b/YamlDotNet.RepresentationModel/Serialization/Serializer.cs @@ -71,14 +71,19 @@ namespace YamlDotNet.RepresentationModel.Serialization /// public sealed class Serializer { - private readonly IList converters = new List(); + internal IList Converters { get; private set; } + + public Serializer() + { + Converters = new List(); + } /// /// Registers a type converter to be used to serialize and deserialize specific types. /// public void RegisterTypeConverter(IYamlTypeConverter converter) { - converters.Add(converter); + Converters.Add(converter); } /// @@ -155,7 +160,7 @@ namespace YamlDotNet.RepresentationModel.Serialization { IObjectGraphVisitor emittingVisitor = new EmittingObjectGraphVisitor(eventEmitter); - emittingVisitor = new CustomSerializationObjectGraphVisitor(emitter, emittingVisitor, converters); + emittingVisitor = new CustomSerializationObjectGraphVisitor(emitter, emittingVisitor, Converters); if ((options & SerializationOptions.DisableAliases) == 0) { @@ -191,11 +196,11 @@ namespace YamlDotNet.RepresentationModel.Serialization { if ((options & SerializationOptions.Roundtrip) != 0) { - return new RoundtripObjectGraphTraversalStrategy(50); + return new RoundtripObjectGraphTraversalStrategy(this, 50); } else { - return new FullObjectGraphTraversalStrategy(50); + return new FullObjectGraphTraversalStrategy(this, 50); } } } diff --git a/YamlDotNet.UnitTests/RepresentationModel/SerializationTests.cs b/YamlDotNet.UnitTests/RepresentationModel/SerializationTests.cs index 9e0a973..6a25ebe 100644 --- a/YamlDotNet.UnitTests/RepresentationModel/SerializationTests.cs +++ b/YamlDotNet.UnitTests/RepresentationModel/SerializationTests.cs @@ -23,6 +23,7 @@ using System; using System.Drawing; using Xunit; using System.IO; +using YamlDotNet.Core; using YamlDotNet.RepresentationModel.Serialization; using System.Reflection; using System.Collections; @@ -524,15 +525,47 @@ namespace YamlDotNet.UnitTests.RepresentationModel #endregion } - //[Fact] - //public void DeserializeTypeConverter() - //{ - // YamlSerializer serializer = new YamlSerializer(); - // object result = serializer.Deserialize(YamlFile("converter.yaml")); + class SomeCustomeType + { + // Test specifically with no parameterless, supposed to fail unless a type converter is specified + public SomeCustomeType(string value) { Value = value; } + public string Value; + } - // Assert.True(typeof(Z).IsAssignableFrom(result.GetType())); - // Assert.Equal("[hello, world]", ((Z)result).aaa, "The property has the wrong value."); - //} + public class CustomTypeConverter : IYamlTypeConverter + { + public bool Accepts(Type type) { return type == typeof(SomeCustomeType); } + + public object ReadYaml(Parser parser, Type type) + { + var value = ((Scalar)parser.Current).Value; + parser.MoveNext(); + return new SomeCustomeType(value); + } + + public void WriteYaml(Emitter emitter, object value, Type type) + { + emitter.Emit(new Scalar(((SomeCustomeType)value).Value)); + } + } + + [Fact] + public void RoundtripWithTypeConverter() + { + SomeCustomeType x = new SomeCustomeType("Yo"); + var serializer = new Serializer(); + serializer.RegisterTypeConverter(new CustomTypeConverter()); + StringWriter buffer = new StringWriter(); + serializer.Serialize(buffer, x, SerializationOptions.Roundtrip); + + Console.WriteLine(buffer.ToString()); + + var deserializer = new YamlSerializer(YamlSerializerModes.Roundtrip); + deserializer.RegisterTypeConverter(new CustomTypeConverter()); + + var copy = deserializer.Deserialize(new StringReader(buffer.ToString())); + Assert.Equal("Yo", copy.Value); + } [Fact] public void RoundtripDictionary()