NewtonSoft JsonConverter - Rename property to value of other property - c#

I have this class:
public class ValueInteger
{
[JsonIgnore]
public string ValueName { get; set; }
public int Value { get; set; }
[JsonProperty("timestamp")]
public UInt64 TimeStamp { get; set; }
}
Given an instance of:
var valueInt = new ValueInteger
{
ValueName = "mycounter",
Value = 7,
TimeStamp = 1010101010
}
It should serialize to:
{ mycounter: 7, timestamp = 1010101010 }
It would be cool if one could declare the Value property as
[JsonRedirect(titlePropertyName: nameof(ValueName))]
public int Value { get; set; }
I probably have to implement my own ContractResolver, and have studiet this post: https://stackoverflow.com/a/47872645/304820
but it depends on the IValueProvider, and AFAIK there is no INameProvider to use for renaming.
Usually renaming is done per class, not per instance.

My approach to this would be to write my own Converter. The converter simply serializes in the same fashion as a normal converter, but whenever it comes across a special attribute on a property, it should rename that property in the output.
So, serializing a C# object would go like this:
First convert the C# object into a JSON object (i.e. JTokens structure).
Run through the properties in the C# object and find the ones that need to be renamed...
For each of those properties, determine what their current name is and what their new name should be.
Perform the renaming on the JSON object.
Finally serialize the JSON object into a string.
I have made a simple implementation of this. The usage looks like this:
class Program
{
static void Main(string[] args)
{
var valueInt = new ValueInteger
{
ValueName = "mycounter",
Value = 7,
TimeStamp = 1010101010
};
var settings = new JsonSerializerSettings { Converters = new JsonConverter[] { new DynamicNameConverter() } };
var result = JsonConvert.SerializeObject(valueInt, settings);
Console.WriteLine(result);
Console.Read();
}
}
public class ValueInteger
{
[JsonIgnore]
public string ValueName { get; set; }
[JsonDynamicName(nameof(ValueName))]
public int Value { get; set; }
[JsonProperty("timestamp")]
public UInt64 TimeStamp { get; set; }
}
And the helper classes:
class DynamicNameConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
// Only use this converter for classes that contain properties with an JsonDynamicNameAttribute.
return objectType.IsClass && objectType.GetProperties().Any(prop => prop.CustomAttributes.Any(attr => attr.AttributeType == typeof(JsonDynamicNameAttribute)));
}
public override bool CanRead => false;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// We do not support deserialization.
throw new NotImplementedException();
}
public override bool CanWrite => true;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var token = JToken.FromObject(value);
if (token.Type != JTokenType.Object)
{
// We should never reach this point because CanConvert() only allows objects with JsonPropertyDynamicNameAttribute to pass through.
throw new Exception("Token to be serialized was unexpectedly not an object.");
}
JObject o = (JObject)token;
var propertiesWithDynamicNameAttribute = value.GetType().GetProperties().Where(
prop => prop.CustomAttributes.Any(attr => attr.AttributeType == typeof(JsonDynamicNameAttribute))
);
foreach (var property in propertiesWithDynamicNameAttribute)
{
var dynamicAttributeData = property.CustomAttributes.FirstOrDefault(attr => attr.AttributeType == typeof(JsonDynamicNameAttribute));
// Determine what we should rename the property from and to.
var currentName = property.Name;
var propertyNameContainingNewName = (string)dynamicAttributeData.ConstructorArguments[0].Value;
var newName = (string)value.GetType().GetProperty(propertyNameContainingNewName).GetValue(value);
// Perform the renaming in the JSON object.
var currentJsonPropertyValue = o[currentName];
var newJsonProperty = new JProperty(newName, currentJsonPropertyValue);
currentJsonPropertyValue.Parent.Replace(newJsonProperty);
}
token.WriteTo(writer);
}
}
[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
class JsonDynamicNameAttribute : Attribute
{
public string ObjectPropertyName { get; }
public JsonDynamicNameAttribute(string objectPropertyName)
{
ObjectPropertyName = objectPropertyName;
}
}
Please be aware that a lot of error handling could be put into DynamicNameConverter but I have left it out to make it easier to read and understand.

Related

How to deserealize a list of type interface with Newtonsoft.Json? [duplicate]

I would like to serialize this code via json.net:
public interface ITestInterface
{
string Guid {get;set;}
}
public class TestClassThatImplementsTestInterface1
{
public string Guid { get;set; }
}
public class TestClassThatImplementsTestInterface2
{
public string Guid { get;set; }
}
public class ClassToSerializeViaJson
{
public ClassToSerializeViaJson()
{
this.CollectionToSerialize = new List<ITestInterface>();
this.CollectionToSerialize.add( new TestClassThatImplementsTestInterface2() );
this.CollectionToSerialize.add( new TestClassThatImplementsTestInterface2() );
}
List<ITestInterface> CollectionToSerialize { get;set; }
}
I want to serialize/deserialize ClassToSerializeViaJson with json.net. Serialization is working, but deserialization gives me this error:
Newtonsoft.Json.JsonSerializationException: Could not create an instance of type ITestInterface. Type is an interface or abstract class and cannot be instantiated.
So how can I deserialize the List<ITestInterface> collection?
I found this question while trying to do this myself. After I implemented Piotr Stapp's(Garath's) answer, I was struck by how simple it seemed. If I was merely implementing a method that was already being passed the exact Type (as a string) that I wanted to instantiate, why wasn't the library binding it automatically?
I actually found that I didn't need any custom binders, Json.Net was able to do exactly what I needed, provided I told it that was what I was doing.
When serializing:
string serializedJson = JsonConvert.SerializeObject(objectToSerialize, Formatting.Indented, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects,
TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple
});
When de-serializing:
var deserializedObject = JsonConvert.DeserializeObject<ClassToSerializeViaJson>(serializedJson, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects
});
Relevant documentation: Serialization Settings for Json.NET and TypeNameHandling setting
Bellow full working example with what you want to do:
public interface ITestInterface
{
string Guid { get; set; }
}
public class TestClassThatImplementsTestInterface1 : ITestInterface
{
public string Guid { get; set; }
public string Something1 { get; set; }
}
public class TestClassThatImplementsTestInterface2 : ITestInterface
{
public string Guid { get; set; }
public string Something2 { get; set; }
}
public class ClassToSerializeViaJson
{
public ClassToSerializeViaJson()
{
this.CollectionToSerialize = new List<ITestInterface>();
}
public List<ITestInterface> CollectionToSerialize { get; set; }
}
public class TypeNameSerializationBinder : SerializationBinder
{
public string TypeFormat { get; private set; }
public TypeNameSerializationBinder(string typeFormat)
{
TypeFormat = typeFormat;
}
public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
assemblyName = null;
typeName = serializedType.Name;
}
public override Type BindToType(string assemblyName, string typeName)
{
var resolvedTypeName = string.Format(TypeFormat, typeName);
return Type.GetType(resolvedTypeName, true);
}
}
class Program
{
static void Main()
{
var binder = new TypeNameSerializationBinder("ConsoleApplication.{0}, ConsoleApplication");
var toserialize = new ClassToSerializeViaJson();
toserialize.CollectionToSerialize.Add(
new TestClassThatImplementsTestInterface1()
{
Guid = Guid.NewGuid().ToString(), Something1 = "Some1"
});
toserialize.CollectionToSerialize.Add(
new TestClassThatImplementsTestInterface2()
{
Guid = Guid.NewGuid().ToString(), Something2 = "Some2"
});
string json = JsonConvert.SerializeObject(toserialize, Formatting.Indented,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
Binder = binder
});
var obj = JsonConvert.DeserializeObject<ClassToSerializeViaJson>(json,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
Binder = binder
});
Console.ReadLine();
}
}
I was also surprised by the simplicity in Garath's, and also came to the conclusion that the Json library can do it automatically. But I also figured that it's even simpler than Ben Jenkinson's answer (even though I can see it has been modified by the developer of the json library himself). From my testings, all you need to do is set TypeNameHandling to Auto, like this:
var objectToSerialize = new List<IFoo>();
// TODO: Add objects to list
var jsonString = JsonConvert.SerializeObject(objectToSerialize,
new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto });
var deserializedObject = JsonConvert.DeserializeObject<List<IFoo>>(jsonString,
new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto });
From TypeNameHandling Enumeration documentation
Auto: Include the .NET type name when the type of the object being
serialized is not the same as its declared type. Note that this
doesn't include the root serialized object by default.
Using the default settings, you cannot. JSON.NET has no way of knowing how to deserialize an array. However, you can specify which type converter to use for your interface type. To see how to do this, see this page: http://blog.greatrexpectations.com/2012/08/30/deserializing-interface-properties-using-json-net/
You can also find information about this problem at this SO question: Casting interfaces for deserialization in JSON.NET
This is an old question, but thought I'd add a more in-depth answer (in the form of an article I wrote): http://skrift.io/articles/archive/bulletproof-interface-deserialization-in-jsonnet/
TLDR: Rather than configure Json.NET to embed type names in the serialized JSON, you can use a JSON converter to figure out which class to deserialize to using whatever custom logic you like.
This has the advantage that you can refactor your types without worrying about deserialization breaking.
It can be done with JSON.NET and JsonSubTypes attributes:
[JsonConverter(typeof(JsonSubtypes))]
[JsonSubtypes.KnownSubTypeWithProperty(typeof(Test1), "Something1")]
[JsonSubtypes.KnownSubTypeWithProperty(typeof(Test2), "Something2")]
public interface ITestInterface
{
string Guid { get; set; }
}
public class Test1 : ITestInterface
{
public string Guid { get; set; }
public string Something1 { get; set; }
}
public class Test2 : ITestInterface
{
public string Guid { get; set; }
public string Something2 { get; set; }
}
and simply:
var fromCode = new List<ITestInterface>();
// TODO: Add objects to list
var json = JsonConvert.SerializeObject(fromCode);
var fromJson = JsonConvert.DeserializeObject<List<ITestInterface>>(json);
I wanted to deserialize JSON that wasn't serialized by my application, hence I needed to specify the concrete implementation manually. I have expanded on Nicholas's answer.
Lets say we have
public class Person
{
public ILocation Location { get;set; }
}
and the concrete instance of
public class Location: ILocation
{
public string Address1 { get; set; }
// etc
}
Add in this class
public class ConfigConverter<I, T> : JsonConverter
{
public override bool CanWrite => false;
public override bool CanRead => true;
public override bool CanConvert(Type objectType)
{
return objectType == typeof(I);
}
public override void WriteJson(JsonWriter writer,
object value, JsonSerializer serializer)
{
throw new InvalidOperationException("Use default serialization.");
}
public override object ReadJson(JsonReader reader,
Type objectType, object existingValue,
JsonSerializer serializer)
{
var jsonObject = JObject.Load(reader);
var deserialized = (T)Activator.CreateInstance(typeof(T));
serializer.Populate(jsonObject.CreateReader(), deserialized);
return deserialized;
}
}
Then define your interfaces with the JsonConverter attribute
public class Person
{
[JsonConverter(typeof(ConfigConverter<ILocation, Location>))]
public ILocation Location { get;set; }
}
Near-duplicate of Inrego's answer, but it's worthy of further explanation:
If you use TypeNameHandling.Auto then it only includes the type/assembly name when it needs to (i.e. interfaces and base/derived classes). So your JSON is cleaner, smaller, more specific.
Which isn't that one of the main selling points of it over XML/SOAP?
Avoid TypeNameHandling.Auto when possible, particularly with user-controllable values.
You will need to write your own deserializer for the collection type.
Rather than repeat others who have already posted boilerplate converter code (particularly Nicholas Westby, whose blog post was quite useful and is linked above), I have included the relevant changes for deserializing a collection of interfaces (I had an enum interface property to distinguish implementors):
public override object ReadJson(JsonReader reader,
Type objectType, object existingValue,
JsonSerializer serializer)
{
Collection<T> result = new Collection<T>();
var array = JArray.Load(reader);
foreach (JObject jsonObject in array)
{
var rule = default(T);
var value = jsonObject.Value<string>("MyDistinguisher");
MyEnum distinguisher;
Enum.TryParse(value, out distinguisher);
switch (distinguisher)
{
case MyEnum.Value1:
rule = serializer.Deserialize<Type1>(jsonObject.CreateReader());
break;
case MyEnum.Value2:
rule = serializer.Deserialize<Type2>(jsonObject.CreateReader());
break;
default:
rule = serializer.Deserialize<Type3>(jsonObject.CreateReader());
break;
}
result.Add(rule);
}
return result;
}
I hope this is helpful to the next person looking for an interface collection deserializer.

How to define string to object/dictionary in JSON

I create a class for define my request, I don't get the accepted JSON string
I define this object:
public class Request
{
public Var_Args[] var_args { get; set; }
}
public class Var_Args
{
public object title { get; set; }
public object owner { get; set; }
}
when I convert it to json, I get the following string:
{"requests":[{"var_args":[{"title":"Test","owner":"skaner"}]}]}
how can I define the class, for get the accepted json string:
{"requests":[{"var_args":[{"title":"Test"},{"owner":"skaner"}]}]}
You can write a custom JSON converter that can serialize every property of an object (of a known type) into a different JSON object.
public class PropertyAsObjectConverter : JsonConverter
{
private readonly Type[] _types;
public PropertyAsObjectConverter(params Type[] types)
{
_types = types;
}
public override bool CanConvert(Type objectType)
{
return _types.Any(t => t == 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)
{
var properties = value.GetType().GetProperties(BindingFlags.Public|BindingFlags.Instance);
foreach(var property in properties)
{
var name = property.Name;
var attrs = property.GetCustomAttributes(typeof(JsonPropertyAttribute));
if(attrs != null)
{
if (attrs.FirstOrDefault() is JsonPropertyAttribute attr)
name = attr.PropertyName;
}
writer.WriteStartObject();
writer.WritePropertyName(name);
serializer.Serialize(writer, property.GetValue(value));
writer.WriteEndObject();
}
}
}
This implements only the serialization, but you can extend it to support deserialization too. You can also extend it to serialize fields should you need that.
You can then define your classes as follows. Notice that I am using JsonPropertyAttribute here to specify the name in the serialized JSON.
public class Content
{
[JsonProperty("requests")]
public Request Value { get; set; }
}
public class Request
{
[JsonProperty("var_args")]
public VarArgs[] Arguments { get; set; }
}
public class VarArgs
{
[JsonProperty("title")]
public object Title { get; set; }
[JsonProperty("owner")]
public object Owner { get; set; }
}
This is how you can use it:
static void Main(string[] args)
{
var request = new Content()
{
Value = new Request()
{
Arguments = new VarArgs[]
{
new VarArgs()
{
Title = "Test",
Owner = "Skaner",
}
}
}
};
var text = JsonConvert.SerializeObject(
request,
Formatting.None,
new PropertyAsObjectConverter(typeof(VarArgs)));
Console.WriteLine(text);
}
The output for this sample is the one you expect:
{"requests":{"var_args":[{"title":"Test"},{"owner":"Skaner"}]}}
You could use a custom JsonConverter like the below.
It takes the Var_Args object and splits it in two different JObject which correspond to two different JSON objects.
public class VarArgsConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var obj = (JObject)JToken.FromObject(value);
var objTitle = new JObject();
objTitle.Add("title", obj.GetValue("title"));
var objOwner = new JObject();
objOwner.Add("owner", obj.GetValue("owner"));
objTitle.WriteTo(writer);
objOwner.WriteTo(writer);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
}
public override bool CanRead
{
get { return false; }
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Var_Args);
}
}
public class Wrapper
{
[JsonProperty("requests")]
public Request Requests { get; set; }
}
public class Request
{
public Var_Args[] var_args { get; set; }
}
public class Var_Args
{
public object title { get; set; }
public object owner { get; set; }
}
Then use it:
var wrapper = new Wrapper();
var request = new Request();
request.var_args = new Var_Args[] {
new Var_Args(){ title = "Test", owner = "skaner" },
new Var_Args(){ title = "Test2", owner = "skaner2" }
};
wrapper.Requests = request;
var serialized = JsonConvert.SerializeObject(wrapper, new VarArgsConverter());
Output
{"requests":{"var_args":[{"title":"Test"},{"owner":"skaner"},{"title":"Test2"},{"owner":"skaner2"}]}}
Note: I'm using the Wrapper class just to produce the requested JSON.
If you don't want to specify the converter each time, you can register your converter globally. Please see this answer which explains how you can do that. So, the serializer will use your custom JsonConverter every time you try to serialize a Var_Args object.
If you register the JsonConvert globally you can use:
var serialized = JsonConvert.SerializeObject(wrapper);
You can use System.Reflection to redefine Var_Args as an implementation of the IEnumerable<Dictionary<string,object>> interface by adding two methods to the class:
public class Var_Args : IEnumerable<Dictionary<string,object>>
{
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
public IEnumerator<Dictionary<string,object>> GetEnumerator()
{
var Properties = GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var Property in Properties)
{
var Entry = new Dictionary<string,object>();
Entry.Add(Property.Name, Property.GetValue(this));
yield return Entry;
}
}
public object title { get; set; }
public object owner { get; set; }
}
While Reflection may be regarded as slow, there is a technique you can use to statically compile an IEnumerable at runtime so that the reflection only occurs once for the definition of the class, like this:
public class Var_Args : IEnumerable<Dictionary<string,object>>
{
private struct PropertyList<T>
{
public static readonly List<Func<T,Dictionary<string,object>>> PropertyGetters;
static PropertyList()
{
PropertyGetters = new List<Func<T,Dictionary<string,object>>>();
var Properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var Property in Properties)
{
var Args = new [] { Expression.Parameter(typeof(T)) };
var Key = Property.Name;
var Value = Expression.Property(Args[0], Property);
Func<T,object> Get = Expression.Lambda<Func<T,object>>(Value, Args).Compile();
PropertyGetters.Add(obj =>
{
var entry = new Dictionary<string,object>();
entry.Add(Key, Get(obj));
return entry;
});
}
}
}
protected static IEnumerable<Dictionary<string,object>> GetPropertiesAsEntries<T>(T obj)
{
return PropertyList<T>.PropertyGetters.Select(f => f(obj));
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
public IEnumerator<Dictionary<string,object>> GetEnumerator()
{
return GetPropertiesAsEntries(this).GetEnumerator();
}
public object title { get; set; }
public object owner { get; set; }
}

JArray into Object with Attributes

I need to convert the following JArray into an Object.
My problem is, that i need to do this with Attributes.
Because i'm not able to change the following call in a decoupled class:
var message = JsonConvert.DeserializeObject(e.Message, messageType)
The JSON
[71,[[77372266,1508067366000,-0.12144759,5517.4],[77372265,1508067366000,-0.020001,5518.2],[77372251,1508067362000,-0.01,5517.2],[77372235,1508067358000,0.020001,5517.8],[77372220,1508067352000,-0.41883579,5517.3],[77372218,1508067352000,-1.05740887,5517.2],[77372211,1508067351000,-1.8084,5517.2],[77372207,1508067350000,-0.01565918,5517.2],[77372206,1508067349000,-0.01721768,5514.3],[77372205,1508067349000,-0.10347625,5514.3],[77372201,1508067348000,-1.08183286,5513.1],[77372200,1508067348000,-0.04218858,5513.1],[77372199,1508067348000,-0.020001,5513.3],[77372198,1508067348000,-0.020001,5513.3],[77372197,1508067348000,-0.020001,5513.3],[77372196,1508067348000,-0.020001,5513.3],[77372195,1508067348000,-2.16396207,5513.3],[77372194,1508067348000,-1.5601,5513.5],[77372193,1508067348000,-0.13520397,5514.2],[77372192,1508067347000,-0.01479603,5514.2],[77372191,1508067347000,-0.13659854,5513.4],[77372189,1508067346000,-0.01597635,5513.3],[77372187,1508067346000,-0.19812221,5513.3],[77372184,1508067346000,-0.01261482,5513.3],[77372183,1508067346000,0.131033,5519.7],[77372182,1508067346000,0.2011,5519.4],[77372181,1508067346000,0.074337,5518.2],[77372180,1508067346000,0.125,5517.5],[77372179,1508067346000,0.06401,5516.5],[77372178,1508067346000,0.05305,5516.3]]]
My Classes
public class Root
{
public int ChannelId { get; set; }
public List<Item> Data { get; set; }
}
public class Item
{
public int Id { get; set; }
public long Timestamp { get; set; }
public decimal Price { get; set; }
public decimal Size { get; set; }
}
If you use the Newtonsoft JSON library (nowadays referred to as JSON.NET) you can use the Jsonconverter class to create your own custom converter.
Your class would then look something like:
public class MyCustomConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(Root).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.StartArray)
{
JArray rootToken = JArray.Load(reader);
if (rootToken[0] != null && rootToken[1] != null)
{
var root = new Root(rootToken[0].Value<int>(), this.readItems(rootToken[1]));
return root;
}
}
return existingValue;
}
private IList<Item> readItems(JToken items)
{
var itemList = new List<Item>();
if (items.Type == JTokenType.Array)
{
foreach(var item in items.Children())
{
if (item.Type == JTokenType.Array && item.Count() == 4)
{
itemList.Add(new Item(
item[0].Value<int>(),
item[1].Value<long>(),
item[2].Value<decimal>(),
item[3].Value<decimal>()));
}
}
}
return itemList;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Note that this custom converter in the current state is only capable of transforming JSON to an object, if you want it to be able to transform a Root object to a JSON string you need to implement the WriteJson method yourself.
You said that because of a decoupled dependency you cannot change the way the callee calls the deserialize method. What you can do is overwrite the JSONConvert.DefaultSettings property and tell it explicitly to use the custom converter.
JsonConvert.DefaultSettings = (() =>
{
var settings = new JsonSerializerSettings();
settings.Converters.Add(new MyCustomConverter());
return settings;
});
var jsonString = "...";
var root = JsonConvert.DeserializeObject(jsonString, typeof(Root));
First parse your json to JArray and then you can simply create a function to parse it into an object of your choice. You can try this:
string json = "[71,[[77372266,1508067366000,-0.12144759,5517.4],[77372265,1508067366000,-0.020001,5518.2],[77372251,1508067362000,-0.01,5517.2],[77372235,1508067358000,0.020001,5517.8],[77372220,1508067352000,-0.41883579,5517.3],[77372218,1508067352000,-1.05740887,5517.2],[77372211,1508067351000,-1.8084,5517.2],[77372207,1508067350000,-0.01565918,5517.2],[77372206,1508067349000,-0.01721768,5514.3],[77372205,1508067349000,-0.10347625,5514.3],[77372201,1508067348000,-1.08183286,5513.1],[77372200,1508067348000,-0.04218858,5513.1],[77372199,1508067348000,-0.020001,5513.3],[77372198,1508067348000,-0.020001,5513.3],[77372197,1508067348000,-0.020001,5513.3],[77372196,1508067348000,-0.020001,5513.3],[77372195,1508067348000,-2.16396207,5513.3],[77372194,1508067348000,-1.5601,5513.5],[77372193,1508067348000,-0.13520397,5514.2],[77372192,1508067347000,-0.01479603,5514.2],[77372191,1508067347000,-0.13659854,5513.4],[77372189,1508067346000,-0.01597635,5513.3],[77372187,1508067346000,-0.19812221,5513.3],[77372184,1508067346000,-0.01261482,5513.3],[77372183,1508067346000,0.131033,5519.7],[77372182,1508067346000,0.2011,5519.4],[77372181,1508067346000,0.074337,5518.2],[77372180,1508067346000,0.125,5517.5],[77372179,1508067346000,0.06401,5516.5],[77372178,1508067346000,0.05305,5516.3]]]";
var array = JArray.Parse(json);
var channelId = array[0];
var listData = array[1].ToList();
Root root = new Root();
root.ChannelId = Convert.ToInt32(channelId);
root.Data = new List<Item>();
for (int i = 0; i < listData.Count; i++)
{
Item newItem = new Item();
var item = listData[i].ToList();
newItem.Id = Convert.ToInt32(item[0]);
newItem.Timestamp = Convert.ToInt64(item[1]);
newItem.Price = Convert.ToDecimal(item[2]);
newItem.Size = Convert.ToDecimal(item[3]);
root.Data.Add(newItem);
}

Deserializing Indexed Properties into an Array

Say I have some Json that will come in a packet like this:
{
"LinkType1": "google",
"LinkUrl1": "https://plus.google.com/test",
"LinkShow1": 1,
"LinkType2": "facebook",
"LinkUrl2": "https://www.facebook.com/test",
"LinkShow2": 0,
"LinkType3": "linkedin",
"LinkUrl3": "http://www.linkedin.com/test",
"LinkShow3": 1,
"count": 3,
"errorCode": 0,
"errorMessage": "Success"
}
Notice how everything comes back as the same property, but with an index on it?
I would love to be able to deserialize that data as though it was an array instead of single properties. What would be the best method for deserializing this into the classes below? I'm using the Newtonsoft Json library for serialization, so a solution using that would be preferred.
public class LinksResult
{
public List<LinkData> Links { get; set; }
[JsonProperty("count")]
public int Count { get; set; }
[JsonProperty("errorCode")]
public int ErrorCode { get; set; }
[JsonProperty("errorMessage")]
public string ErrorMessage { get; set; }
}
public class LinkData
{
public string LinkType { get; set; }
public string LinkUrl { get; set; }
public bool LinkShow { get; set; }
}
You can use a custom JsonConverter to deserialize the JSON data into the structure that you want. Here is what the code for the converter might look like.
class LinksResultConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(LinksResult));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject obj = JObject.Load(reader);
LinksResult result = new LinksResult();
result.Count = (int)obj["count"];
result.ErrorCode = (int)obj["errorCode"];
result.ErrorMessage = (string)obj["errorMessage"];
result.Links = new List<LinkData>();
for (int i = 1; i <= result.Count; i++)
{
string index = i.ToString();
LinkData link = new LinkData();
link.LinkType = (string)obj["LinkType" + index];
link.LinkUrl = (string)obj["LinkUrl" + index];
link.LinkShow = (int)obj["LinkShow" + index] == 1;
result.Links.Add(link);
}
return result;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
To use the converter, just add a [JsonConverter] attribute to your LinksResult class as shown below. (Note that you don't need the [JsonProperty] attributes with this approach, since the mapping between JSON property names and the actual class members is handled directly by the converter.)
[JsonConverter(typeof(LinksResultConverter))]
public class LinksResult
{
public List<LinkData> Links { get; set; }
public int Count { get; set; }
public int ErrorCode { get; set; }
public string ErrorMessage { get; set; }
}
Then, you can deserialize like this:
LinksResult result = JsonConvert.DeserializeObject<LinksResult>(json);
Fiddle: https://dotnetfiddle.net/56b34H
Brian's answer was very good and it got me 80% of the way to where I wanted to be. However it's not a very good implementation to use over and over again if this sort of pattern happens on many different objects.
I made something more generic. An interface that a "Page" would have.
public interface IPage<TItem>
{
int Count { get; set; }
List<TItem> PageItems { get; set; }
}
Then the Page converter itself.
public class PageConverter<TPage, TItem> : JsonConverter
where TPage : IPage<TItem>, new()
where TItem : new()
{
private readonly Regex _numberPostfixRegex = new Regex(#"\d+$");
public override bool CanWrite
{
get { return false; }
}
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(TPage));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var obj = serializer.Deserialize<JObject>(reader);
var page = new TPage();
serializer.Populate(obj.CreateReader(), page); //Loads everything that isn't a part of the items.
page.PageItems = new List<TItem>();
for (int i = 1; i <= page.Count; i++)
{
string index = i.ToString();
//Find all properties that have a number at the end, then any of those that are the same number as the current index.
//Put those in a new JObject.
var jsonItem = new JObject();
foreach (var prop in obj.Properties().Where(p => _numberPostfixRegex.Match(p.Name).Value == index))
{
jsonItem[_numberPostfixRegex.Replace(prop.Name, "")] = prop.Value;
}
//Deserialize and add to the list.
TItem item = jsonItem.ToObject<TItem>(serializer);
page.PageItems.Add(item);
}
return page;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
So then all that's needed is to implement it on the links result:
[JsonConverter(typeof(PageConverter<LinksResult, LinkData>))]
public class LinksResult : IPage<LinkData>
{
public int Count { get; set; }
public List<LinkData> PageItems { get; set; }
}
I figured out you can control the serialization of capitalization with JsonSerializerSettings, so best leave that detail up to the chosen serializer, not my converter.
Fiddle here: https://dotnetfiddle.net/7KhwYY
Here is similar solution you may apply. See Serialize json to an object with catch all dictionary property The answer by David Hoerster.

Deserialize collection of interface-instances?

I would like to serialize this code via json.net:
public interface ITestInterface
{
string Guid {get;set;}
}
public class TestClassThatImplementsTestInterface1
{
public string Guid { get;set; }
}
public class TestClassThatImplementsTestInterface2
{
public string Guid { get;set; }
}
public class ClassToSerializeViaJson
{
public ClassToSerializeViaJson()
{
this.CollectionToSerialize = new List<ITestInterface>();
this.CollectionToSerialize.add( new TestClassThatImplementsTestInterface2() );
this.CollectionToSerialize.add( new TestClassThatImplementsTestInterface2() );
}
List<ITestInterface> CollectionToSerialize { get;set; }
}
I want to serialize/deserialize ClassToSerializeViaJson with json.net. Serialization is working, but deserialization gives me this error:
Newtonsoft.Json.JsonSerializationException: Could not create an instance of type ITestInterface. Type is an interface or abstract class and cannot be instantiated.
So how can I deserialize the List<ITestInterface> collection?
I found this question while trying to do this myself. After I implemented Piotr Stapp's(Garath's) answer, I was struck by how simple it seemed. If I was merely implementing a method that was already being passed the exact Type (as a string) that I wanted to instantiate, why wasn't the library binding it automatically?
I actually found that I didn't need any custom binders, Json.Net was able to do exactly what I needed, provided I told it that was what I was doing.
When serializing:
string serializedJson = JsonConvert.SerializeObject(objectToSerialize, Formatting.Indented, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects,
TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple
});
When de-serializing:
var deserializedObject = JsonConvert.DeserializeObject<ClassToSerializeViaJson>(serializedJson, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects
});
Relevant documentation: Serialization Settings for Json.NET and TypeNameHandling setting
Bellow full working example with what you want to do:
public interface ITestInterface
{
string Guid { get; set; }
}
public class TestClassThatImplementsTestInterface1 : ITestInterface
{
public string Guid { get; set; }
public string Something1 { get; set; }
}
public class TestClassThatImplementsTestInterface2 : ITestInterface
{
public string Guid { get; set; }
public string Something2 { get; set; }
}
public class ClassToSerializeViaJson
{
public ClassToSerializeViaJson()
{
this.CollectionToSerialize = new List<ITestInterface>();
}
public List<ITestInterface> CollectionToSerialize { get; set; }
}
public class TypeNameSerializationBinder : SerializationBinder
{
public string TypeFormat { get; private set; }
public TypeNameSerializationBinder(string typeFormat)
{
TypeFormat = typeFormat;
}
public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
assemblyName = null;
typeName = serializedType.Name;
}
public override Type BindToType(string assemblyName, string typeName)
{
var resolvedTypeName = string.Format(TypeFormat, typeName);
return Type.GetType(resolvedTypeName, true);
}
}
class Program
{
static void Main()
{
var binder = new TypeNameSerializationBinder("ConsoleApplication.{0}, ConsoleApplication");
var toserialize = new ClassToSerializeViaJson();
toserialize.CollectionToSerialize.Add(
new TestClassThatImplementsTestInterface1()
{
Guid = Guid.NewGuid().ToString(), Something1 = "Some1"
});
toserialize.CollectionToSerialize.Add(
new TestClassThatImplementsTestInterface2()
{
Guid = Guid.NewGuid().ToString(), Something2 = "Some2"
});
string json = JsonConvert.SerializeObject(toserialize, Formatting.Indented,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
Binder = binder
});
var obj = JsonConvert.DeserializeObject<ClassToSerializeViaJson>(json,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
Binder = binder
});
Console.ReadLine();
}
}
I was also surprised by the simplicity in Garath's, and also came to the conclusion that the Json library can do it automatically. But I also figured that it's even simpler than Ben Jenkinson's answer (even though I can see it has been modified by the developer of the json library himself). From my testings, all you need to do is set TypeNameHandling to Auto, like this:
var objectToSerialize = new List<IFoo>();
// TODO: Add objects to list
var jsonString = JsonConvert.SerializeObject(objectToSerialize,
new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto });
var deserializedObject = JsonConvert.DeserializeObject<List<IFoo>>(jsonString,
new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto });
From TypeNameHandling Enumeration documentation
Auto: Include the .NET type name when the type of the object being
serialized is not the same as its declared type. Note that this
doesn't include the root serialized object by default.
Using the default settings, you cannot. JSON.NET has no way of knowing how to deserialize an array. However, you can specify which type converter to use for your interface type. To see how to do this, see this page: http://blog.greatrexpectations.com/2012/08/30/deserializing-interface-properties-using-json-net/
You can also find information about this problem at this SO question: Casting interfaces for deserialization in JSON.NET
This is an old question, but thought I'd add a more in-depth answer (in the form of an article I wrote): http://skrift.io/articles/archive/bulletproof-interface-deserialization-in-jsonnet/
TLDR: Rather than configure Json.NET to embed type names in the serialized JSON, you can use a JSON converter to figure out which class to deserialize to using whatever custom logic you like.
This has the advantage that you can refactor your types without worrying about deserialization breaking.
It can be done with JSON.NET and JsonSubTypes attributes:
[JsonConverter(typeof(JsonSubtypes))]
[JsonSubtypes.KnownSubTypeWithProperty(typeof(Test1), "Something1")]
[JsonSubtypes.KnownSubTypeWithProperty(typeof(Test2), "Something2")]
public interface ITestInterface
{
string Guid { get; set; }
}
public class Test1 : ITestInterface
{
public string Guid { get; set; }
public string Something1 { get; set; }
}
public class Test2 : ITestInterface
{
public string Guid { get; set; }
public string Something2 { get; set; }
}
and simply:
var fromCode = new List<ITestInterface>();
// TODO: Add objects to list
var json = JsonConvert.SerializeObject(fromCode);
var fromJson = JsonConvert.DeserializeObject<List<ITestInterface>>(json);
I wanted to deserialize JSON that wasn't serialized by my application, hence I needed to specify the concrete implementation manually. I have expanded on Nicholas's answer.
Lets say we have
public class Person
{
public ILocation Location { get;set; }
}
and the concrete instance of
public class Location: ILocation
{
public string Address1 { get; set; }
// etc
}
Add in this class
public class ConfigConverter<I, T> : JsonConverter
{
public override bool CanWrite => false;
public override bool CanRead => true;
public override bool CanConvert(Type objectType)
{
return objectType == typeof(I);
}
public override void WriteJson(JsonWriter writer,
object value, JsonSerializer serializer)
{
throw new InvalidOperationException("Use default serialization.");
}
public override object ReadJson(JsonReader reader,
Type objectType, object existingValue,
JsonSerializer serializer)
{
var jsonObject = JObject.Load(reader);
var deserialized = (T)Activator.CreateInstance(typeof(T));
serializer.Populate(jsonObject.CreateReader(), deserialized);
return deserialized;
}
}
Then define your interfaces with the JsonConverter attribute
public class Person
{
[JsonConverter(typeof(ConfigConverter<ILocation, Location>))]
public ILocation Location { get;set; }
}
Near-duplicate of Inrego's answer, but it's worthy of further explanation:
If you use TypeNameHandling.Auto then it only includes the type/assembly name when it needs to (i.e. interfaces and base/derived classes). So your JSON is cleaner, smaller, more specific.
Which isn't that one of the main selling points of it over XML/SOAP?
Avoid TypeNameHandling.Auto when possible, particularly with user-controllable values.
You will need to write your own deserializer for the collection type.
Rather than repeat others who have already posted boilerplate converter code (particularly Nicholas Westby, whose blog post was quite useful and is linked above), I have included the relevant changes for deserializing a collection of interfaces (I had an enum interface property to distinguish implementors):
public override object ReadJson(JsonReader reader,
Type objectType, object existingValue,
JsonSerializer serializer)
{
Collection<T> result = new Collection<T>();
var array = JArray.Load(reader);
foreach (JObject jsonObject in array)
{
var rule = default(T);
var value = jsonObject.Value<string>("MyDistinguisher");
MyEnum distinguisher;
Enum.TryParse(value, out distinguisher);
switch (distinguisher)
{
case MyEnum.Value1:
rule = serializer.Deserialize<Type1>(jsonObject.CreateReader());
break;
case MyEnum.Value2:
rule = serializer.Deserialize<Type2>(jsonObject.CreateReader());
break;
default:
rule = serializer.Deserialize<Type3>(jsonObject.CreateReader());
break;
}
result.Add(rule);
}
return result;
}
I hope this is helpful to the next person looking for an interface collection deserializer.

Categories