I have a simple use case where I want to deserialize a JSON that is basically an array of items, items are not identical but they all share a base class.
UPDATE:
my technical limitations:
I receive the JSON from a webhook and can't alter the serialization code or inject
any tokens in the source JSON
The Type property is the only information to do the correspondence between the derived class that I want to deserialize to
need strongly typed instances and not dynamic ones
The classes are in an assembly and I can't add any Json Annotations
Here is the code:
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
public class Program
{
public static void Main()
{
var json = #" [{""Type"":0},{""Name"":""Derived"",""Type"":1}]";
var deserializedInstances = JsonConvert.DeserializeObject<List<BaseClass>>(json);
foreach(var e in deserializedInstances) {
if(e is BaseClass baseClass)
{
Console.WriteLine("Base Class , Type = {0}", baseClass.Type);
}else if(e is DerviedClass derivedClass)
{
Console.WriteLine("Derived Class , Type = {0}, Name = {1}", derivedClass.Type, derivedClass.Name);
}
}
// Output
// Base Class , Type = 0
// Base Class , Type = 0
}
public class BaseClass
{
public virtual int Type =>0;
}
public class DerviedClass: BaseClass
{
public string Name {get; set;}
public override int Type =>1;
}
}
so this code will produce this output:
// Base Class , Type = 0
// Base Class , Type = 0
but in my case, I want to have the instance of the derived class.
// Base Class , Type = 0
// Base Class , Type = 1, Name = "Derived"
What is the best way to achieve this in terms of performance?
As Jawad pointed out, you are deserializing to the BaseClass so the objects will also be of type BaseClass and not extend beyond that.
What you want to do is something akin to this answer: Json.net serialize/deserialize derived types?
The short answer is you have to account for settings when deserializing, more specifically Type Name handling. Copied from the answer:
Base object1 = new Base() { Name = "Object1" };
Derived object2 = new Derived() { Something = "Some other thing" };
List<Base> inheritanceList = new List<Base>() { object1, object2 };
JsonSerializerSettings settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
string Serialized = JsonConvert.SerializeObject(inheritanceList, settings);
List<Base> deserializedList = JsonConvert.DeserializeObject<List<Base>>(Serialized, settings);
This will allow the ability to deserialize into derived classes and alike. An example can be found here as well: https://www.newtonsoft.com/json/help/html/SerializeTypeNameHandling.htm
EDIT: In terms of performance I'm not sure there's a much better way of producing the same results that you are looking for.
Taking a detour over dynamic to strongly typed:
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
public class Program
{
public static void Main()
{
var a = new BaseClass(){ Type = 0 };
var b = new DerivedClass(){ Type = 1, Name = "Hello" };
var list = new List<BaseClass>(){a,b};
var json = JsonConvert.SerializeObject(list);
Console.WriteLine(json);
var intermediate = JsonConvert.DeserializeObject<List<dynamic>>(json);
var result = new List<object>();
foreach( dynamic item in intermediate )
{
// Of course, you surely could optimize the conversion:
if( item.Type == 0 ) result.Add( new BaseClass(){Type = item.Type});
if( item.Type == 1 ) result.Add( new DerivedClass(){Type= item.Type, Name= item.Name});
}
Console.WriteLine($"[{string.Join(", ",result)}]");
}
}
public class BaseClass
{
public int Type {get; set;}
}
public class DerivedClass: BaseClass
{
public string Name {get; set;}
}
produces output on fiddle:
[{"Type":0},{"Name":"Hello","Type":1}]
[BaseClass, DerviedClass]
Mind, that this is just a proof of concept. Of course, you'd need to fortify and find some decent algorithm to get from dynamic to your desired strong type.
Update
This fiddle shows some possibilities to improve on effort for many Derived Classes: https://dotnetfiddle.net/zECBx5
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
public class Program
{
public static void Main()
{
var a = new BaseClass(){ Type = 0 };
var b = new DerivedClass(){ Type = 1, Name = "Hello" };
var list = new List<BaseClass>(){a,b};
var json = JsonConvert.SerializeObject(list);
Console.WriteLine(json);
var intermediate = JsonConvert.DeserializeObject<List<dynamic>>(json);
var result = Construct( intermediate );
Console.WriteLine($"[{string.Join(", ",result.Select(x => x?.ToString() ?? "NULL"))}]");
}
public static List<object> Construct( List<dynamic> items )
{
var result = new List<object>();
Console.WriteLine( $"Processing {items.Count} dynamic items" );
foreach( dynamic item in items )
{
result.Add(Construct( item ));
}
return result;
}
private static Dictionary<int, Func<dynamic, object>> factoryMap = new () {
{0 , Construct<BaseClass>},
{1 , Construct<DerivedClass>},
};
public static object Construct( dynamic item )
{
Console.WriteLine($"Item Type = {item.Type}");
object result = null;
result = factoryMap[(int)item.Type](item);
return result;
}
public static TResult Construct<TResult>( dynamic item ) where TResult: class, new()
{
Console.WriteLine($"Constructing a {typeof(TResult).ToString()}");
TResult result = new();
foreach( var property in result.GetType().GetProperties() )
{
JObject jo = item as JObject;
var propVal = jo.Property(property.Name).ToObject(property.PropertyType);
Console.WriteLine($"Setting property {property.Name} to value {propVal}");
property.SetValue( result, propVal );
}
return result;
}
}
public class BaseClass
{
public int Type {get; set;}
}
public class DerivedClass: BaseClass
{
public string Name {get; set;}
}
Custom Converters is the solution I was looking for:
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
public class Program
{
public static void Main()
{
var json = #" [{""Type"":0},{""Name"":""Derived"",""Type"":1}]";
var deserializedInstances = JsonConvert.DeserializeObject<List<BaseClass>>(json,
new JsonSerializerSettings()
{
Converters = { new CustomConverter() }
});
foreach(var e in deserializedInstances) {
if(e is BaseClass baseClass && e.Type == 0)
{
Console.WriteLine("Base Class , Type = {0}", baseClass.Type);
}
if(e is DerviedClass derivedClass)
{
Console.WriteLine("Derived Class , Type = {0}, Name = {1}", derivedClass.Type, derivedClass.Name);
}
}
// output
// Base Class , Type = 0
// Derived Class , Type = 1, Name = Derived
}
public class BaseClass
{
public int Type {get; set;}
}
public class DerviedClass: BaseClass
{
public string Name {get; set;}
}
public class CustomConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(BaseClass).IsAssignableFrom(objectType) || typeof(BaseClass) == objectType;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jo = JObject.Load(reader);
if (jo["Type"].Value<int>() == 0)
return new BaseClass() { Type = jo["Type"].Value<int>()}; // avoid stack overflow
if (jo["Type"].Value<int>() == 1)
return jo.ToObject<DerviedClass>(serializer);
return null;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
}
Related
So let's say I have a:
List<IInterface> list;
that has been serialized with TypeNameHandling.Auto, so it has "dynamic" type information. I can deserialize it fine as Newtonsoft.Json can recognize the type from the $type and Json can use the correct constructor. So far so good.
Now say I want to override the creation converter with a mehtod:
CustomCreationConverter<IInterface>
that overrides the creation of the object:
public override IInterface Create(Type objectType)
At this point objectType will always be IInterface and not a derived implementation, so I have no way to create the correct object. The meta-information of $type is now lost.
Is there an elegant way to fix this?
Here would be an attempt that does not work:
public class CustomConverter : CustomCreationConverter<Example.IInterface> {
public override Example.IInterface Create(Type objectType) {
return Example.MakeObject(objectType); // this won't work, objectType will always be IInterface
}
}
public class Example {
public interface IInterface { };
public class A : IInterface { public int content; };
public class B : IInterface { public float data; };
public static IInterface MakeObject(Type t) {
if (t == typeof(IInterface)) {
throw new Exception();
}
return t == typeof(A) ? new A() : new B();
}
public static void Serialize() {
var settings = new JsonSerializerSettings() {
TypeNameHandling = TypeNameHandling.Auto
};
JsonSerializer serializer = JsonSerializer.Create(settings);
// serializer.Converters.Add(new CustomConverter()); // ?? can't have both, either CustomConverter or $type
List<IInterface> list = new() { MakeObject(typeof(A)), MakeObject(typeof(B)) };
using (StreamWriter sw = new("example.json")) {
serializer.Serialize(sw, list);
}
// Now read back example.json into a List<IInterface> using MakeObject
// Using CustomConverter won't work
using (JsonTextReader rd = new JsonTextReader(new StreamReader("example.json"))) {
List<IInterface> list2 = serializer.Deserialize<List<IInterface>>(rd);
}
}
}
Once you provide a custom converter such as CustomCreationConverter<T> for a type, the converter is responsible for all the deserialization logic including logic for type selection logic that would normally be implemented by TypeNameHandling. If you only want to inject a custom factory creation method and leave all the rest of the deserialization logic unchanged, you could create your own custom contract resolver and inject the factory method as JsonContract.DefaultCreator.
To implement this, first define the following factory interface and contract resolver:
public interface IObjectFactory<out T>
{
bool CanCreate(Type type);
T Create(Type type);
}
public class ObjectFactoryContractResolver : DefaultContractResolver
{
readonly IObjectFactory<object> factory;
public ObjectFactoryContractResolver(IObjectFactory<object> factory) => this.factory = factory ?? throw new ArgumentNullException(nameof(factory));
protected override JsonContract CreateContract(Type objectType)
{
var contract = base.CreateContract(objectType);
if (factory.CanCreate(objectType))
{
contract.DefaultCreator = () => factory.Create(objectType);
contract.DefaultCreatorNonPublic = false;
}
return contract;
}
}
Next, refactor your IInterface class hierarchy to make use of an IObjectFactory as an object creation factory:
public class InterfaceFactory : IObjectFactory<IInterface>
{
public InterfaceFactory(string runtimeId) => this.RuntimeId = runtimeId; // Some value to inject into the constructor
string RuntimeId { get; }
public bool CanCreate(Type type) => !type.IsAbstract && typeof(IInterface).IsAssignableFrom(type);
public IInterface Create(Type type) => type switch
{
var t when t == typeof(A) => new A(RuntimeId),
var t when t == typeof(B) => new B(RuntimeId),
_ => throw new NotImplementedException(type.ToString()),
};
}
public interface IInterface
{
public string RuntimeId { get; }
}
public class A : IInterface
{
[JsonIgnore] public string RuntimeId { get; }
internal A(string id) => this.RuntimeId = id;
public int content { get; set; }
}
public class B : IInterface
{
[JsonIgnore] public string RuntimeId { get; }
internal B(string id) => this.RuntimeId = id;
public float data { get; set; }
}
(Here RuntimeId is some value that needs to be injected during object creation.)
Now you will be able to construct your list as follows:
var valueToInject = "some value to inject";
var factory = new InterfaceFactory(valueToInject);
List<IInterface> list = new() { factory.Create(typeof(A)), factory.Create(typeof(B)) };
And serialize and deserialize as follows:
var resolver = new ObjectFactoryContractResolver(factory)
{
// Set any necessary properties e.g.
NamingStrategy = new CamelCaseNamingStrategy(),
};
var settings = new JsonSerializerSettings
{
ContractResolver = resolver,
TypeNameHandling = TypeNameHandling.Auto,
};
var json = JsonConvert.SerializeObject(list, Formatting.Indented, settings);
var list2 = JsonConvert.DeserializeObject<List<IInterface>>(json, settings);
Notes:
Newtosoft recommends that you cache and reuse your contract resolvers for best performance.
Newtonsoft also recommends that
TypeNameHandling should be used with caution when your application deserializes JSON from an external source. Incoming types should be validated with a custom SerializationBinder when deserializing with a value other than None.
For why, see e.g. TypeNameHandling caution in Newtonsoft Json or External json vulnerable because of Json.Net TypeNameHandling auto?.
Demo fiddle here.
Just want to stress out this question is irrelevant of why this technology and why not that. This is related to C# code.
I searched below but none provides solution.
Serialize a class that implements IEnumerable
Serialization on classes that implement IEnumerator
I am working on WCF Rest project in C# which is replacing .NET Remoting communication layer. I use Newtonsoft dll to serialize and de-serialize.
I have a class called MyDTO which implements IEnumerator, IEnumerable which I cannot change since this is old class and many applications in production use them. When I try to serialize MyDTO into string I do not get any error/exception message, I just get an empty array like "[]". Can anybody tell how we can serialize/deserialze class that implements IEnumerator, IEnumerable?
public class MyDTO : IEnumerator, IEnumerable
I am calling a method called ABC in OldServer.dll which gives me MyDTO object. I want to convert this to string and again from string to MyDTO.
Please let me know if you need more information. Please see below for MyDTO class which I cannot change:
[Serializable]
public class MyDTO : IEnumerator, IEnumerable
{
#region Inner Types
internal class MyObjComparer : IComparer<MyObj>
{
public int Compare(MyObj x, MyObj y)
{
return x.InputCode.CompareTo(y.InputCode);
}
}
#endregion
#region Variables
private List<MyObj> myObjList;
private string selectedObjectMessage;
private bool containSequenceNo = false;
private bool sortEnabled;
private bool filterEnabled;
private IComparer<MyObj> objectComparer;
#endregion
#region Constructors
public MyDTO()
{
this.myObjList = new List<MyObj>();
this.selectedObjectMessage = string.Empty;
}
public MyDTO(List<MyObj> objects)
{
this.myObjList = objects;
this.selectedObjectMessage = string.Empty;
}
public MyDTO(IComparer<MyObj> argSortComparer)
: this()
{
this.objectComparer = argSortComparer;
}
public MyDTO(List<MyObj> argErrors, IComparer<MyObj> argSortComparer)
: this(argErrors)
{
this.objectComparer = argSortComparer;
}
public MyDTO(List<MyObj> argErrors, IComparer<MyObj> argSortComparer, bool argSortEnabled)
: this(argErrors, argSortComparer)
{
this.sortEnabled = argSortEnabled;
}
#endregion
#region Properties
public string selectedObjectMessage
{
get { return this.selectedObjectMessage; }
set
{
if (value == null)
value = string.Empty;
this.selectedObjectMessage = value;
}
}
public int Count
{
get { return this.myObjList.Count; }
}
public bool ErrorsContainsSequenceNo
{
get { return this.containSequenceNo; }
set { this.containSequenceNo = value; }
}
public List<MyObj> myObjList
{
get { return this.myObjList; }
set { this.myObjList = value; }
}
public MyDTO WithoutEmptyMyObjects
{
get
{
MyDTO objs = new MyDTO();
foreach (MyObj obj in this.myObjList)
{
if (!string.IsNullOrEmpty(obj.InputCode))
objs.Add(obj);
}
return objs;
}
}
#endregion
}
UPDATE 1:
After spending almost a day we decided to write our own method targeting MyDTO which will produce XmlDocument which is serialize. Using that same XmlDocument we will try create MyDTO object. I feel we need to just concentrate on properties that has setters. I will let you know.
Using the almighty Json.Net
1) Create a contract resolver to serialize/deserialize specific members based on their accessibility, cause by default serialization/deserialization only happens for public members.
public class MyContractResolver : Newtonsoft.Json.Serialization.DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var props = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(p => base.CreateProperty(p, memberSerialization))
.Union(type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(f => base.CreateProperty(f, memberSerialization)))
.ToList();
props.ForEach(p => { p.Writable = true; p.Readable = true; });
return props;
}
}
2) Then you can serialize your object like
var settings = new JsonSerializerSettings() { ContractResolver = new MyContractResolver() };
// dtoInstance is your dto instance you wish to serialize.
string serializedDTO = JsonConvert.SerializeObject(dtoInstance, settings);
3) And you can deserialize like
var settings = new JsonSerializerSettings() { ContractResolver = new MyContractResolver() };
MyDTO deserializedDTO = JsonConvert.DeserializeObject<MyDTO>(serializedDTO, settings);
While playing around Stack collection in C# I encountered the following issue. Exactly I am not sure why it is happening. Please put some light on the reason and alternative to the solution.
Problem -
A class having Stack as property. For example name that class as Progress. T is of class type Item.
Now whenever the user makes any progress we will be storing in stack. And if user leaves in between, then next time we will peek the item from the stack so from that stage. Below code snippet will give an idea of what is being tried...
using static System.Console;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace StackCollection
{
class Program
{
static void Main(string[] args)
{
Progress progress = new Progress();
progress.Items.Push(new Item { PlanID = null, PlanName = "Plan A" });
var jsonString = JsonConvert.SerializeObject(progress);
var temp = JsonConvert.DeserializeObject<Progress>(jsonString);
temp.Items.Push(new Item { PlanID = null, PlanName = "Plan B" });
jsonString = JsonConvert.SerializeObject(temp);
temp = JsonConvert.DeserializeObject<Progress>(jsonString);
temp.Items.Push(new Item { PlanID = null, PlanName = "Plan C" });
jsonString = JsonConvert.SerializeObject(temp);
temp = JsonConvert.DeserializeObject<Progress>(jsonString);
WriteLine(temp.Items.Peek().PlanName);
ReadLine();
}
}
class Progress
{
public Stack<Item> Items { get; set; }
public Progress()
{
Items = new Stack<Item>();
}
}
class Item
{
public string PlanID { get; set; }
public string PlanName { get; set; }
}
}
now the line -
WriteLine(temp.Items.Peek().PlanName);
should return
Plan C
but it is returning
Plan B
So, why the index is being changed, any clue or pointer will be helpful.
Since this is a known behavior of Json.NET, as noted by this answer, a custom JsonConverter can be used when deserializing a stack that pushes items on in the correct order.
The following universal converter can be used with Stack<T> for any T:
/// <summary>
/// Converter for any Stack<T> that prevents Json.NET from reversing its order when deserializing.
/// </summary>
public class StackConverter : JsonConverter
{
// Prevent Json.NET from reversing the order of a Stack<T> when deserializing.
// https://github.com/JamesNK/Newtonsoft.Json/issues/971
static Type StackParameterType(Type objectType)
{
while (objectType != null)
{
if (objectType.IsGenericType)
{
var genericType = objectType.GetGenericTypeDefinition();
if (genericType == typeof(Stack<>))
return objectType.GetGenericArguments()[0];
}
objectType = objectType.BaseType;
}
return null;
}
public override bool CanConvert(Type objectType)
{
return StackParameterType(objectType) != null;
}
object ReadJsonGeneric<T>(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var list = serializer.Deserialize<List<T>>(reader);
var stack = existingValue as Stack<T> ?? (Stack<T>)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
for (int i = list.Count - 1; i >= 0; i--)
stack.Push(list[i]);
return stack;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
try
{
var parameterType = StackParameterType(objectType);
var method = GetType().GetMethod("ReadJsonGeneric", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
var genericMethod = method.MakeGenericMethod(new[] { parameterType });
return genericMethod.Invoke(this, new object[] { reader, objectType, existingValue, serializer });
}
catch (TargetInvocationException ex)
{
// Wrap the TargetInvocationException in a JsonSerializerException
throw new JsonSerializationException("Failed to deserialize " + objectType, ex);
}
}
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then add it to JsonSerializerSettings to correct the ordering of stacks when deserializing:
var settings = new JsonSerializerSettings { Converters = new[] { new StackConverter() } };
var jsonString = JsonConvert.SerializeObject(progress, settings);
var temp = JsonConvert.DeserializeObject<Progress>(jsonString, settings);
Or mark the Stack<T> property directly with [JsonConverter(typeof(StackConverter))]:
class Progress
{
[JsonConverter(typeof(StackConverter))]
public Stack<Item> Items { get; set; }
public Progress()
{
Items = new Stack<Item>();
}
}
It seems like the stack is being serialized as a List. The problem is that this does not preserve the proper order when deconstructing the stack (the items are actually pushed in the reverse order). Here's a quick workaround to this issue:
using System;
using static System.Console;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using Newtonsoft.Json;
namespace StackCollection
{
class Program
{
static void Main(string[] args)
{
Progress progress = new Progress();
progress.Items.Push(new Item { PlanID = null, PlanName = "Plan A" });
var jsonString = JsonConvert.SerializeObject(progress);
var temp = JsonConvert.DeserializeObject<Progress>(jsonString);
temp.Items.Push(new Item { PlanID = null, PlanName = "Plan B" });
jsonString = JsonConvert.SerializeObject(temp);
temp = JsonConvert.DeserializeObject<Progress>(jsonString);
temp.Items.Push(new Item { PlanID = null, PlanName = "Plan C" });
jsonString = JsonConvert.SerializeObject(temp);
temp = JsonConvert.DeserializeObject<Progress>(jsonString);
WriteLine(temp.Items.Peek().PlanName);
ReadLine();
}
}
class Progress
{
[JsonIgnore]
public Stack<Item> Items { get; set; }
public List<Item> ItemList { get; set; }
[OnSerializing]
internal void OnSerializing(StreamingContext context)
{
ItemList = Items?.ToList();
}
[OnDeserialized]
internal void OnDeserialized(StreamingContext context)
{
ItemList?.Reverse();
Items = new Stack<Item>(ItemList ?? Enumerable.Empty<Item>());
}
public Progress()
{
Items = new Stack<Item>();
}
}
class Item
{
public string PlanID { get; set; }
public string PlanName { get; set; }
}
}
If you try to debug it then you will notice that items order is broken after Stack deserialization.
The same question has been asked on JSON.NET GitHub issue tracker a month ago.
The answer from JamesNK:
I'm afraid this is a limition of a Stack. The results returned when it is serialized and the opposite order for when it is deserialized.
In Visual Studio 2019, this C# works:
List<string> ls = null;
Stack<string> ss = null;
if (json != null)
{
ls = JsonConvert.DeserializeObject<List<string>>(json);
ss = new Stack<string>(ls);
}
(This is an edit of the answer from here, which originally had an errant Reverse method call on the list that caused the opposite of the desired result.)
I get the following error with the code below:
"An object reference is required for the non-static field, method, or
property 'Response.PropName'"
Code:
public class Response<T> : Response
{
private string PropName
{
get
{
return typeof(T).Name;
}
}
[JsonProperty(PropName)]
public T Data { get; set; }
}
What you're trying to do is possible, but not trivial, and can't be done with only the built-in attributes from JSON.NET. You'll need a custom attribute, and a custom contract resolver.
Here's the solution I came up with:
Declare this custom attribute:
[AttributeUsage(AttributeTargets.Property)]
class JsonPropertyGenericTypeNameAttribute : Attribute
{
public int TypeParameterPosition { get; }
public JsonPropertyGenericTypeNameAttribute(int position)
{
TypeParameterPosition = position;
}
}
Apply it to your Data property
public class Response<T> : Response
{
[JsonPropertyGenericTypeName(0)]
public T Data { get; set; }
}
(0 is the position of T in Response<T>'s generic type parameters)
Declare the following contract resolver, which will look for the JsonPropertyGenericTypeName attribute and get the actual name of the type argument:
class GenericTypeNameContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var prop = base.CreateProperty(member, memberSerialization);
var attr = member.GetCustomAttribute<JsonPropertyGenericTypeNameAttribute>();
if (attr != null)
{
var type = member.DeclaringType;
if (!type.IsGenericType)
throw new InvalidOperationException($"{type} is not a generic type");
if (type.IsGenericTypeDefinition)
throw new InvalidOperationException($"{type} is a generic type definition, it must be a constructed generic type");
var typeArgs = type.GetGenericArguments();
if (attr.TypeParameterPosition >= typeArgs.Length)
throw new ArgumentException($"Can't get type argument at position {attr.TypeParameterPosition}; {type} has only {typeArgs.Length} type arguments");
prop.PropertyName = typeArgs[attr.TypeParameterPosition].Name;
}
return prop;
}
}
Serialize with this resolver in your serialization settings:
var settings = new JsonSerializerSettings { ContractResolver = new GenericTypeNameContractResolver() };
string json = JsonConvert.SerializeObject(response, settings);
This will give the following output for Response<Foo>
{
"Foo": {
"Id": 0,
"Name": null
}
}
Here's a potentially easier way to achieve it. All you need to do is to have Response extend JObject, like this:
public class Response<T>: Newtonsoft.Json.Linq.JObject
{
private static string TypeName = (typeof(T)).Name;
private T _data;
public T Data {
get { return _data; }
set {
_data = value;
this[TypeName] = Newtonsoft.Json.Linq.JToken.FromObject(_data);
}
}
}
If you do that, the following would work as you expect:
static void Main(string[] args)
{
var p1 = new Response<Int32>();
p1.Data = 5;
var p2 = new Response<string>();
p2.Data = "Message";
Console.Out.WriteLine("First: " + JsonConvert.SerializeObject(p1));
Console.Out.WriteLine("Second: " + JsonConvert.SerializeObject(p2));
}
Output:
First: {"Int32":5}
Second: {"String":"Message"}
In case you can't have Response<T> extend JObject, because you really need it to extend Response, you could have Response itself extend JObject, and then have Response<T> extend Response as before. It should work just the same.
#Thomas Levesque: OK. So let's say that you can't extend JObject in Response<T> because you need to extend a pre-existing Response class. Here's another way you could implement the same solution:
public class Payload<T> : Newtonsoft.Json.Linq.JObject {
private static string TypeName = (typeof(T)).Name;
private T _data;
public T Data {
get { return _data; }
set {
_data = value;
this[TypeName] = Newtonsoft.Json.Linq.JToken.FromObject(_data);
}
}
}
//Response is a pre-existing class...
public class Response<T>: Response {
private Payload<T> Value;
public Response(T arg) {
Value = new Payload<T>() { Data = arg };
}
public static implicit operator JObject(Response<T> arg) {
return arg.Value;
}
public string Serialize() {
return Value.ToString();
}
}
So now there are the following options to Serialize the class:
static void Main(string[] args) {
var p1 = new Response<Int32>(5);
var p2 = new Response<string>("Message");
JObject p3 = new Response<double>(0.0);
var p4 = (JObject) new Response<DateTime>(DateTime.Now);
Console.Out.WriteLine(p1.Serialize());
Console.Out.WriteLine(p2.Serialize());
Console.Out.WriteLine(JsonConvert.SerializeObject(p3));
Console.Out.WriteLine(JsonConvert.SerializeObject(p4));
}
The Output will look something like this:
{"Int32":5}
{"String":"Message"}
{"Double":0.0}
{"DateTime":"2016-08-25T00:18:31.4882199-04:00"}
I am attempting to serialize and deserialize a complex object graph:
Class A contains an read only property containing an immutable array of objects of type B. The objects of type B, as well as the immutable array, are created in the constructor of type A.
Other types contain references to objects of type B that are obtained by accessing the array of an object of type A.
During deserialization, I need any references to a B to end up pointing at the appropriate object created by the A constructor by index, rather than making brand new B objects from JSON. I'm trying to use PreserveReferencesHandling with JSON.NET. This understandably does not work, because it attempts to use deserialized versions of B rather than the A-constructed versions.
Is there another strategy I can use here without modifying my types?
Edit: To clarify and make extremely clear, the solution must not modify the type itself. You can touch the contract resolver, binder, reference resolver, etc. but not the type. Also, B types cannot be deserialized. They must be made by A's constructor.
Update
Your question doesn't give an example of what you are trying to accomplish, so I'm guessing about some of your design requirements. To confirm, your situation is:
You have some complex graph of objects to serialize with Json.NET
Throughout the graph, there are many instances of class A.
A contains an immutable array of instances of class B that can only ever be constructed inside the constructor of A.
Each instance of A might or might not have properties to serialize (not specified)
Each instance of B might or might not have properties to serialize (not specified).
Also throughout the graph there are many references to instances of B, but in all cases these references actually point to an instance of B inside one of the instances of A.
When you deserialize your graph, you need all references to an instance of B to to point to an instance of B inside an instance of A corresponding to the original instance, by array index.
You don't have any code to collect and discover all instances of A in your object graph.
You cannot touch the c# code for the classes in any way, not even to add data contract attributes or private properties.
Let's model this situation with the following classes:
public abstract class B
{
public int Index { get; set; } // Some property that could be modified.
}
public class A
{
public class BActual : B
{
}
static int nextId = -1;
readonly B[] items; // A private read-only array that is never changed.
public A()
{
items = Enumerable.Range(101 + 10 * Interlocked.Increment(ref nextId), 2).Select(i => new BActual { Index = i }).ToArray();
}
public string SomeProperty { get; set; }
public IEnumerable<B> Items
{
get
{
foreach (var b in items)
yield return b;
}
}
public string SomeOtherProperty { get; set; }
}
public class MidClass
{
public MidClass()
{
AnotherA = new A();
}
public A AnotherA { get; set; }
}
public class MainClass
{
public MainClass()
{
A1 = new A();
MidClass = new MidClass();
A2 = new A();
}
public List<B> ListOfB { get; set; }
public A A2 { get; set; }
public MidClass MidClass { get; set; }
public A A1 { get; set; }
}
Then, to serialize, you need to use Json.NET to collect all instances of A in your object graph. Next, with PreserveReferencesHandling = PreserveReferencesHandling.Objects set, serialize a proxy class containing a table of all instances of A as the first item, then your root object as the second item.
To deserialize, with PreserveReferencesHandling.Objects you must deserialize your proxy class using a JsonConverter for A that deserializes properties (if any) of A and B, and adds a reference for the serialized "$ref" references to B to the new instances of B allocated in the constructor of A.
Thus:
// Used to enable Json.NET to traverse an object hierarchy without actually writing any data.
public class NullJsonWriter : JsonWriter
{
public NullJsonWriter()
: base()
{
}
public override void Flush()
{
// Do nothing.
}
}
public class TypeInstanceCollector<T> : JsonConverter where T : class
{
readonly List<T> instanceList = new List<T>();
readonly HashSet<T> instances = new HashSet<T>();
public List<T> InstanceList { get { return instanceList; } }
public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}
public override bool CanRead { get { return false; } }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
T instance = (T)value;
if (!instances.Contains(instance))
{
instanceList.Add(instance);
instances.Add(instance);
}
// It's necessary to write SOMETHING here. Null suffices.
writer.WriteNull();
}
}
public class ADeserializer : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(A).IsAssignableFrom(objectType);
}
public override bool CanWrite { get { return false; } }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var obj = JObject.Load(reader);
if (obj == null)
return existingValue;
A a;
var refId = (string)obj["$ref"];
if (refId != null)
{
a = (A)serializer.ReferenceResolver.ResolveReference(serializer, refId);
if (a != null)
return a;
}
a = ((A)existingValue) ?? new A();
var items = obj["Items"];
obj.Remove("Items");
// Populate properties other than the items, if any
// This also updates the ReferenceResolver table.
using (var objReader = obj.CreateReader())
serializer.Populate(objReader, a);
// Populate properties of the B items, if any
if (items != null)
{
if (items.Type != JTokenType.Array)
throw new JsonSerializationException("Items were not an array");
var itemsArray = (JArray)items;
if (a.Items.Count() < itemsArray.Count)
throw new JsonSerializationException("too few items constructucted"); // Item counts must match
foreach (var pair in a.Items.Zip(itemsArray, (b, o) => new { ItemB = b, JObj = o }))
{
#if false
// If your B class has NO properties to deserialize, do this
var id = (string)pair.JObj["$id"];
if (id != null)
serializer.ReferenceResolver.AddReference(serializer, id, pair.ItemB);
#else
// If your B class HAS SOME properties to deserialize, do this
using (var objReader = pair.JObj.CreateReader())
{
// Again, Populate also updates the ReferenceResolver table
serializer.Populate(objReader, pair.ItemB);
}
#endif
}
}
return a;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
public class RootProxy<TRoot, TTableItem>
{
[JsonProperty("table", Order = 1)]
public List<TTableItem> Table { get; set; }
[JsonProperty("data", Order = 2)]
public TRoot Data { get; set; }
}
public class TestClass
{
public static string Serialize(MainClass main)
{
// First, collect all instances of A
var collector = new TypeInstanceCollector<A>();
var collectionSettings = new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects, Converters = new JsonConverter[] { collector } };
using (var jsonWriter = new NullJsonWriter())
{
JsonSerializer.CreateDefault(collectionSettings).Serialize(jsonWriter, main);
}
// Now serialize a proxt class with the collected instances of A at the beginning, to establish reference ids for all instances of B.
var proxy = new RootProxy<MainClass, A> { Data = main, Table = collector.InstanceList };
var serializationSettings = new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects };
return JsonConvert.SerializeObject(proxy, Formatting.Indented, serializationSettings);
}
public static MainClass Deserialize(string json)
{
var serializationSettings = new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects, Converters = new JsonConverter[] { new ADeserializer() } };
var proxy = JsonConvert.DeserializeObject<RootProxy<MainClass, A>>(json, serializationSettings);
return proxy.Data;
}
static IEnumerable<A> GetAllA(MainClass main)
{
// For testing. In your case apparently you can't do this manually.
if (main.A1 != null)
yield return main.A1;
if (main.A2 != null)
yield return main.A2;
if (main.MidClass != null && main.MidClass.AnotherA != null)
yield return main.MidClass.AnotherA;
}
static IEnumerable<B> GetAllB(MainClass main)
{
return GetAllA(main).SelectMany(a => a.Items);
}
public static void Test()
{
var main = new MainClass();
main.A1.SomeProperty = "main.A1.SomeProperty";
main.A1.SomeOtherProperty = "main.A1.SomeOtherProperty";
main.A2.SomeProperty = "main.A2.SomeProperty";
main.A2.SomeOtherProperty = "main.A2.SomeOtherProperty";
main.MidClass.AnotherA.SomeProperty = "main.MidClass.AnotherA.SomeProperty";
main.MidClass.AnotherA.SomeOtherProperty = "main.MidClass.AnotherA.SomeOtherProperty";
main.ListOfB = GetAllB(main).Reverse().ToList();
var json = Serialize(main);
var main2 = Deserialize(json);
var json2 = Serialize(main2);
foreach (var b in main2.ListOfB)
Debug.Assert(GetAllB(main2).Contains(b)); // No assert
Debug.Assert(json == json2); // No assert
Debug.Assert(main.ListOfB.Select(b => b.Index).SequenceEqual(main2.ListOfB.Select(b => b.Index))); // No assert
Debug.Assert(GetAllA(main).Select(a => a.SomeProperty + a.SomeOtherProperty).SequenceEqual(GetAllA(main2).Select(a => a.SomeProperty + a.SomeOtherProperty))); // No assert
}
}
Original Answer
Firstly, you can use the [JsonConstructor] attribute to specify that Json.NET should use a non-default constructor to deserialize your class A. Doing so will allow you to deserialize into your immutable collection. This constructor can be private, so that you can continue to create your instances of B in the pre-existing public constructor. Note that the constructor argument names must match the original property names.
Secondly, if you set PreserveReferencesHandling = PreserveReferencesHandling.Objects, then any other objects in your object graph that refer directly to instances of B held by the immutable array will, when serialized and deserialized, continue to refer directly to the instances in the deserialized immutable array. I.e., it should just work.
Consider the following test case:
public class B
{
public int Index { get; set; }
}
public class A
{
static int nextId = -1;
readonly B [] items; // A private read-only array that is never changed.
[JsonConstructor]
private A(IEnumerable<B> Items, string SomeProperty)
{
this.items = (Items ?? Enumerable.Empty<B>()).ToArray();
this.SomeProperty = SomeProperty;
}
// // Create instances of "B" with different properties each time the default constructor is called.
public A() : this(Enumerable.Range(101 + 10*Interlocked.Increment(ref nextId), 2).Select(i => new B { Index = i }), "foobar")
{
}
public IEnumerable<B> Items
{
get
{
foreach (var b in items)
yield return b;
}
}
[JsonIgnore]
public int Count { get { return items.Length; } }
public B GetItem(int index)
{
return items[index];
}
public string SomeProperty { get; set; }
public string SomeOtherProperty { get; set; }
}
public class TestClass
{
public A A { get; set; }
public List<B> ListOfB { get; set; }
public static void Test()
{
var a = new A() { SomeOtherProperty = "something else" };
var test = new TestClass { A = a, ListOfB = a.Items.Reverse().ToList() };
var settings = new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects };
var json = JsonConvert.SerializeObject(test, Formatting.Indented, settings);
Debug.WriteLine(json);
var test2 = JsonConvert.DeserializeObject<TestClass>(json, settings);
// Assert that pointers in "ListOfB" are equal to pointers in A.Items
Debug.Assert(test2.ListOfB.All(i2 => test2.A.Items.Contains(i2, new ReferenceEqualityComparer<B>())));
// Assert deserialized data is the same as the original data.
Debug.Assert(test2.A.SomeProperty == test.A.SomeProperty);
Debug.Assert(test2.A.SomeOtherProperty == test.A.SomeOtherProperty);
Debug.Assert(test2.A.Items.Select(i => i.Index).SequenceEqual(test.A.Items.Select(i => i.Index)));
var json2 = JsonConvert.SerializeObject(test2, Formatting.Indented, settings);
Debug.WriteLine(json2);
Debug.Assert(json2 == json);
}
}
In this case I have created class B with some data, class A which contains an immutable collection of B which it creates in its public constructor, and an encompassing class TestClass that contains an instance of A and a list of items B taken from A. When I serialize this, I get the following JSON:
{
"$id": "1",
"A": {
"$id": "2",
"Items": [
{
"$id": "3",
"Index": 101
},
{
"$id": "4",
"Index": 102
}
],
"SomeProperty": "foobar",
"SomeOtherProperty": "something else"
},
"ListOfB": [
{
"$ref": "4"
},
{
"$ref": "3"
}
]
}
Then, when I deserialize it, I assert that all deserialized items B in ListOfB have pointer equality with one of the instances of B in a.Items. I also assert that all deserialized properties have the same values as in the originals, thus confirming that the non-default private constructor was called to deserialize the immutable collection.
Is this what you want?
For checking pointer equality of instances of B, I use:
public class ReferenceEqualityComparer<T> : IEqualityComparer<T> where T : class
{
#region IEqualityComparer<T> Members
public bool Equals(T x, T y)
{
return object.ReferenceEquals(x, y);
}
public int GetHashCode(T obj)
{
return (obj == null ? 0 : obj.GetHashCode());
}
#endregion
}