Skip to content
This repository has been archived by the owner on May 16, 2022. It is now read-only.

Patches for skuvault #206

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions sandbox/DynamicCodeDumper/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,14 @@ static void Main(string[] args)
{

#if NET45
var a1 = (DynamicObjectResolver.Default as ISave).Save();
var a2 = (DynamicObjectResolver.ExcludeNull as ISave).Save();
// var a1 = (DynamicObjectResolver.Default as ISave).Save();
// var a2 = (DynamicObjectResolver.ExcludeNull as ISave).Save();
//var a2 = DynamicUnionResolver.Instance.Save();
//var a3 = DynamicEnumResolver.Instance.Save();
//var a4 = DynamicContractlessObjectResolver.Instance.Save();
var a3 = DynamicCompositeResolver.Save();
// var a3 = DynamicCompositeResolver.Save();

Verify(a2);
// Verify(a2);
#endif
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0;NET45;NET47</TargetFrameworks>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>opensource.snk</AssemblyOriginatorKeyFile>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion src/Utf8Json.UnityShims/Utf8Json.UnityShims.csproj
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0;NET45;NET47</TargetFrameworks>
<Configurations>Debug;Release</Configurations>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>opensource.snk</AssemblyOriginatorKeyFile>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
Expand Down
6 changes: 6 additions & 0 deletions src/Utf8Json/Attributes.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Text;
using Utf8Json.Formatters;

namespace Utf8Json
{
Expand All @@ -22,6 +23,11 @@ public JsonFormatterAttribute(Type formatterType, params object[] arguments)
}
}

[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class PolymorphicFormatterAttribute : Attribute
{
}

[AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = true)]
public class SerializationConstructorAttribute : Attribute
{
Expand Down
77 changes: 77 additions & 0 deletions src/Utf8Json/Formatters/PolymorphicFormatter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace Utf8Json.Formatters
{
public class PolymorphicFormatter<T> : IJsonFormatter<T>
{
private readonly IReadOnlyDictionary<string, Type> _derivedTypesByName =
typeof(T).Assembly.GetTypes().Where(t => typeof(T).IsAssignableFrom(t)).ToDictionary(t => t.FullName);

public void Serialize(ref JsonWriter writer, T value, IJsonFormatterResolver formatterResolver)
{
if (value == null)
{
writer.WriteNull();
}
else
{
var instanceType = value.GetType();
if (!_derivedTypesByName.ContainsKey(instanceType.FullName))
{
throw new InvalidOperationException("Trying to serialize unexpected type {0} derived from {1}");
}

writer.WriteBeginObject();

writer.WritePropertyName("$type");
writer.WriteString(instanceType.FullName);
writer.WriteValueSeparator();

var innerFormatter = formatterResolver.GetFormatterDynamic(instanceType);
var specificTypeFormatter = (IJsonFormatter) formatterResolver.GetFormatterDynamic(instanceType);
var serializeMethod = specificTypeFormatter.GetType().GetMethod("Serialize");

writer.WritePropertyName("$value");
var parameters = new object[] {writer, value, formatterResolver};
serializeMethod.Invoke(innerFormatter, parameters);
writer = (JsonWriter) parameters[0]; // pass back updated JsonWriter, which is a struct

writer.WriteEndObject();
}
}

public T Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver)
{
if (reader.ReadIsNull())
{
return default(T);
}

reader.ReadIsBeginObjectWithVerify();
if (reader.ReadPropertyName() != "$type")
{
throw new InvalidOperationException(string.Format("Could not find $type property for {0} message", typeof(T).Name));
}
var typeName = reader.ReadString();

// after read type, need to create dynamic formatter for the type
var instanceType = _derivedTypesByName[typeName];
var specificTypeFormatter = (IJsonFormatter) formatterResolver.GetFormatterDynamic(instanceType);
var deserializeMethod = specificTypeFormatter.GetType().GetMethod("Deserialize");

// then deserialize the whole thing
reader.ReadIsValueSeparatorWithVerify();
if (reader.ReadPropertyName() != "$value")
{
throw new InvalidOperationException(string.Format("Could not find $value property for {0} message", typeof(T).Name));
}
var parameters = new object[] {reader, formatterResolver};
var result = (T) deserializeMethod.Invoke(specificTypeFormatter, parameters);
reader = (JsonReader) parameters[0]; // pass back updated JsonReader, which is a struct
reader.ReadIsEndObjectWithVerify();
return result;
}
}
}
4 changes: 2 additions & 2 deletions src/Utf8Json/Internal/Emit/MetaType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,15 @@ public MetaType(Type type, Func<string, string> nameMutetor, bool allowPrivate)
}

// GetConstructor
var ctor = ti.DeclaredConstructors.Where(x => x.IsPublic)
var ctor = ti.DeclaredConstructors
.SingleOrDefault(x => x.GetCustomAttribute<SerializationConstructorAttribute>(false) != null);
var constructorParameters = new List<MetaMember>();
{
IEnumerator<ConstructorInfo> ctorEnumerator = null;
if (ctor == null)
{
// descending.
ctorEnumerator = ti.DeclaredConstructors.Where(x => x.IsPublic).OrderByDescending(x => x.GetParameters().Length).GetEnumerator();
ctorEnumerator = ti.DeclaredConstructors.Where(c => !c.IsStatic).OrderBy(x => x.GetParameters().Length).GetEnumerator();
if (ctorEnumerator.MoveNext())
{
ctor = ctorEnumerator.Current;
Expand Down
42 changes: 42 additions & 0 deletions src/Utf8Json/Resolvers/PolymorphicFormatterResolver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System.Reflection;
using Utf8Json.Formatters;

namespace Utf8Json.Resolvers
{
/// <summary>
/// Get polymorphic formatter
/// </summary>
public sealed class PolymorphicFormatterResolver : IJsonFormatterResolver
{
public static IJsonFormatterResolver Instance = new PolymorphicFormatterResolver();

PolymorphicFormatterResolver()
{
}

public IJsonFormatter<T> GetFormatter<T>()
{
return FormatterCache<T>.formatter;
}

static class FormatterCache<T>
{
public static readonly IJsonFormatter<T> formatter;

static FormatterCache()
{
#if (UNITY_METRO || UNITY_WSA) && !NETFX_CORE
var attr = (PolymorphicFormatterAttribute)typeof(T).GetCustomAttributes(typeof(PolymorphicFormatterAttribute), true).FirstOrDefault();
#else
var attr = typeof(T).GetTypeInfo().GetCustomAttribute<PolymorphicFormatterAttribute>();
#endif
if (attr == null)
{
return;
}

formatter = new PolymorphicFormatter<T>();
}
}
}
}
3 changes: 2 additions & 1 deletion src/Utf8Json/Resolvers/StandardResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ internal static class StandardResolverHelper
#endif
EnumResolver.Default, // Enum(default => string)
DynamicGenericResolver.Instance, // T[], List<T>, etc...
AttributeFormatterResolver.Instance // [JsonFormatter]
AttributeFormatterResolver.Instance, // [JsonFormatter]
PolymorphicFormatterResolver.Instance // [PolymorphicFormatter]
};
}

Expand Down
2 changes: 1 addition & 1 deletion src/Utf8Json/Utf8Json.csproj
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;NET45;NET47</TargetFrameworks>
<Configurations>Debug;Release;</Configurations>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>opensource.snk</AssemblyOriginatorKeyFile>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU' AND '$(TargetFramework)'=='NET45'">
Expand Down
10 changes: 3 additions & 7 deletions tests/Utf8Json.Tests/ObjectTest1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public class MyInterfaceNonConstructor : IMyInterface

public int MyProperty { get; set; }

MyInterfaceNonConstructor()
private MyInterfaceNonConstructor()
{
this.CalledConstructor = true;
}
Expand Down Expand Up @@ -360,10 +360,8 @@ public void UninitializedObjectCreation()
var bin = JsonSerializer.Serialize(obj, StandardResolver.AllowPrivate);

var d = JsonSerializer.Deserialize<MyAbstructNonConstructor>(bin, StandardResolver.AllowPrivate);
d.CalledConstructor.IsFalse();
d.CalledConstructor.IsTrue();
d.MyProperty = 999;

Assert.Throws<InvalidOperationException>(() => JsonSerializer.Deserialize<MyAbstract>(bin, StandardResolver.AllowPrivate));
}
{
var obj = MyInterfaceNonConstructor.Create();
Expand All @@ -373,10 +371,8 @@ public void UninitializedObjectCreation()
var bin = JsonSerializer.Serialize(obj, StandardResolver.AllowPrivate);

var d = JsonSerializer.Deserialize<MyInterfaceNonConstructor>(bin, StandardResolver.AllowPrivate);
d.CalledConstructor.IsFalse();
d.CalledConstructor.IsTrue();
d.MyProperty = 999;

Assert.Throws<InvalidOperationException>(() => JsonSerializer.Deserialize<IMyInterface>(bin, StandardResolver.AllowPrivate));
}
}

Expand Down
83 changes: 83 additions & 0 deletions tests/Utf8Json.Tests/PolymorphicTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
using System.Collections.Generic;
using Utf8Json.Resolvers;
using Xunit;

namespace Utf8Json.Tests
{
public class PolymorphicTest
{
[Fact]
public void Derived_class_is_serialized_and_deserialized_as_instance_of_base_type()
{
var obj = new Derived
{
BaseProp = "base",
DerivedProp = "derived"
};
JsonSerializer.SetDefaultResolver(StandardResolver.AllowPrivate);
var json = JsonSerializer.Serialize<Base>(obj);
var deserialized = JsonSerializer.Deserialize<Base>(json);

Assert.IsType(typeof(Derived), deserialized);
Assert.Equal(obj.BaseProp, deserialized.BaseProp);
Assert.Equal(obj.DerivedProp, ((Derived)deserialized).DerivedProp);
}

[Fact]
public void Null_is_serialized_and_deserialized_correctly()
{
JsonSerializer.SetDefaultResolver(StandardResolver.AllowPrivate);
var json = JsonSerializer.Serialize<Base>(null);
var deserialized = JsonSerializer.Deserialize<Base>(json);

Assert.Null(deserialized);
}

[Fact]
public void List_of_polymorphic_type_is_serialized_and_deserialized_correctly()
{
var list = new List<Base>
{
new Derived { BaseProp = "base", DerivedProp = "derived"},
new AnotherDerived { BaseProp = "base", AnotherProp = "another"},
null
};
JsonSerializer.SetDefaultResolver(StandardResolver.AllowPrivate);
var json = JsonSerializer.Serialize(list);
var deserialized = JsonSerializer.Deserialize<List<Base>>(json);

Assert.Collection(deserialized,
first =>
{
var derived = first as Derived;
Assert.NotNull(derived);
Assert.Equal(list[0].BaseProp, derived.BaseProp);
Assert.Equal(((Derived)list[0]).DerivedProp, derived.DerivedProp);
},
second =>
{
var another = second as AnotherDerived;
Assert.NotNull(another);
Assert.Equal(list[1].BaseProp, another.BaseProp);
Assert.Equal(((AnotherDerived)list[1]).AnotherProp, another.AnotherProp);
},
Assert.Null);
}
}

[PolymorphicFormatter]
abstract class Base
{
public string BaseProp { get; set; }
}

class Derived : Base
{
public string DerivedProp { get; set; }
}

class AnotherDerived : Base
{
public string AnotherProp { get; set; }
}
}