How to Deserialize Json with Composite Pattern - C#, Json - c#

Since I had to search forever for an answer, that would just work as is, I will provide an example here, when you serialize and deserialize an object with a composite pattern structure.
My problem was deserializing this kind of class structure (https://en.wikipedia.org/wiki/Composite_pattern):
abstract class BaseClass
{
public int Id { get; set; }
}
class Leaf : BaseClass
{
public string Foo { get; set; }
}
class Composite : BaseClass
{
public List<BaseClass> ClassList = new List<BaseClass>();
}
Serialization worked by using:
var composite = new Composite();
JsonConvert.SerializeObject(composite, Formatting.Indented);
Deserialziation didn't work out of box.

Solution for deserialization is to build a CustomConverter:
public class BaseClassJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(BaseClass) == objectType;
}
public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
try
{
var jObject = JObject.Load(reader);
var jsonSerializerSettings = new JsonSerializerSettings();
jsonSerializerSettings.Converters.Add(new BaseClassJsonConverter());
if (jObject.ContainsKey("Foo"))
return JsonConvert.DeserializeObject<Leaf>(jObject.ToString(), jsonSerializerSettings);
else if (jObject.ContainsKey("ClassList"))
return JsonConvert.DeserializeObject<Composite>(jObject.ToString(), jsonSerializerSettings);
else
throw new System.SystemException("Class not implemented");
return null;
}
catch (JsonReaderException)
{
return null;
}
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value,
JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Key difference to other answers I found is that they were missing to put the settings in the ReadJson into the SeserialzieObject again, that way it couldn't handle the deserialization of a recursive structure (Deserializing JSON to abstract class).
Now you either decorate your BaseClass with: [JsonConverter(typeof(BaseConverter))]
Or you call the deserialization with the converter in the settings:
var jsonSerializerSettings = new JsonSerializerSettings();
jsonSerializerSettings.Converters.Add(new BaseClassJsonConverter());
JsonConvert.DeserializeObject<BaseClass>(jsonString, jsonSerializerSettings);

Related

Override StringEnumConverter in JSON.net [duplicate]

I use the following in my Web API project's Startup.cs to JSON-serialize Enums into strings:
// Configure JSON Serialization
var jsonSerializationSettings = GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings;
jsonSerializationSettings.Formatting = Newtonsoft.Json.Formatting.None;
jsonSerializationSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
This is to avoid decorating every Enum property with [JsonConverter(typeof(StringEnumConverter))]
Now, how can I selectively opt out of my global serialization setting for some Enum properties and use the default serializer that converts to integers?
You could add a dummy converter to the properties in question that does nothing:
public class NoConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
// Note - not called when attached directly via [JsonConverter(typeof(NoConverter))]
throw new NotImplementedException();
}
public override bool CanRead { get { return false; } }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then attach it to the property using [JsonConverter(typeof(NoConverter))]. Having done so, the JsonConverter attribute's converter supersedes the globally specified converter, but since CanRead and CanWrite both return false no conversion is performed. For collections of enums, you could use [JsonProperty(ItemConverterType = typeof(NoConverter))].
For instance, if you define the types:
public enum Foo { A, B, C }
public class RootObject
{
[JsonConverter(typeof(NoConverter))]
public Foo FooAsInteger { get; set; }
public Foo FooAsString { get; set; }
}
Then
var root = new RootObject { FooAsInteger = Foo.B, FooAsString = Foo.B };
var json = JsonConvert.SerializeObject(root, Formatting.Indented, new StringEnumConverter());
Console.WriteLine(json);
Produces the output
{
"FooAsInteger": 1,
"FooAsString": "B"
}
Note that you can also apply NoConverter directly to the enum, if you want all occurrences of the enum in all data models to be serialized as an integer:
[JsonConverter(typeof(NoConverter))]
public enum FooAlwaysAsInteger { A, B, C }
Sample fiddle.

Storing original JSON string in deserialised JSON.NET objects

This is basically a follow-on to the question Newtonsoft Object → Get JSON string .
I have an object that looks like this:
[JsonConverter(typeof(MessageConverter))]
public class Message
{
public Message(string original)
{
this.Original = original;
}
public string Type { get; set; }
public string Original { get; set; }
}
My requirement is to store the original JSON string as part of the object when I initialise it. I have been able to (partially) successfully achieve this using a custom JsonConverter and then basically doing something like this in the JsonConverter:
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == Newtonsoft.Json.JsonToken.Null)
return null;
JObject obj = JObject.Load(reader);
return new Message(obj.ToString(Formatting.None))
{
Type = obj["type"].ToString()
};
}
However the problem I run into is when I try to inherit from Message with something like
public class CustomMessage : Message
{
public string Prop1 { get; set; }
}
For obvious reasons my code fails because it tries to return a new Message() not a new CustomMessage().
So short of implementing a big if statement with all my sub-types and manually binding using something like JObject["prop"].ToObject<T>() how can I successfully populate the Original property while still binding all my sub-type values using the default deserialisation?
NOTE: The reason this is being done is because the original message might contain data that isn't actually being bound so I can't just add a property which serialises the object as it stands.
The following solution works
One thing you can do is to decorate each child class by generic JsonConverter attribute.
[JsonConverter(typeof(MessageConverter<Message>))]
public class Message
{
public Message(string original)
{
this.Original = original;
}
public string Type { get; set; }
public string Original { get; set; }
}
[JsonConverter(typeof(MessageConverter<CustomMessage>))]
public class CustomMessage : Message
{
public CustomMessage(string original) : base(original)
{
}
public string Prop1 { get; set; }
}
public class MessageConverter<T> : JsonConverter where T : Message
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == Newtonsoft.Json.JsonToken.Null)
return null;
JObject obj = JObject.Load(reader);
var customObject = JsonConvert.DeserializeObject<T>(obj.ToString(), new JsonSerializerSettings
{
ContractResolver = new CustomContractResolver()
});
customObject.Original = obj.ToString();
return customObject;
}
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
}
//This will remove our declared Converter
public class CustomContractResolver : DefaultContractResolver
{
protected override JsonConverter ResolveContractConverter (Type objectType)
{
return null;
}
}
And then you can use the same serializer for all of the child classes
CustomMessage x = JsonConvert.DeserializeObject<CustomMessage>("{\"type\":\"Test\",\"Prop1\":\"Prop1\"}");
Message y = JsonConvert.DeserializeObject<Message>("{\"type\":\"Test\"}");
Here is the output screen shot
I'm leaving the question open in the hope that someone comes up with a better answer but I've temporarily used the following solution to resolve my problem.
public static class MessageExtensions
{
public static T Deserialize<T>(this string message) where T : Message
{
T instance = Activator.CreateInstance<T>();
instance.Original = message;
JsonConvert.PopulateObject(message, instance);
return instance;
}
}

DeserializeObject array to object gives no result

When I try to deserialize the json I receive from an external source my program won't map the childobjects when I use value.first().
private Class1 class1List;
public List<Class1> Class1List
{
get
{
return new List<Class1> { class1List };
}
set
{
class1List = value.First();
}
}
If I want it to work I have to alter the class1List to a List and have to remove .First(). The problem is that the Listwill always only contain one element and for internal storage and usage it would be best if I can get only the first element and store that as an object. The json I receive cannot be altered. (if Serialize my objects the json looks the same).
{
"Class1List":[
{"Class2List":[
{"Name":"test"}
]}
]}
Edit: Removing the get makes it work
Edit 2 : altering the get to this also makes it work
get
{
if (class1List != null)
{
return new List<Class1> { class1List };
}
return null;
}
If you are that particular not add private member I have tried an another solution
var selctedList = JObject.Parse("YOUR JSON").SelectToken("Class1List").ToString();
var class1List = JsonConvert.DeserializeObject<Class1[]>(selctedList, new Class1Converter());
public class Class1
{
public string Name { get; set; }
}
public class Class1Converter : JsonCreationConverter<Class1>
{
protected override Class1 Create(Type objectType, JObject jObject)
{
return new Class1();
}
}
public abstract class JsonCreationConverter<T> : JsonConverter
{
protected abstract T Create(Type objectType, JObject jObject);
public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
try
{
// Load JObject from stream
JObject jObject = JObject.Load(reader);
var lp = jObject[jObject.Properties().Select(p => p.Name).FirstOrDefault()];
JObject kl = JsonConvert.DeserializeObject<JObject[]>(lp.ToString()).FirstOrDefault();
// Create target object based on JObject
T target = Create(objectType, kl);
// Populate the object properties
serializer.Populate(kl.CreateReader(), target);
return target;
}
catch (Exception er)
{
throw er;
}
}
public override void WriteJson(JsonWriter writer,
object value,
JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Disclaimer: I read it somewhere but I can't find the source now and my explanation might be wrong, but the solution work.
Json.Net require you to return the object that Json.Net created during parsing.
class BaseClass
{
internal Class1 class1List;
private List<Class1> _class1List;
public List<Class1> Class1List
{
get
{
return _class1List;
// return new List<Class1> { class1List };
}
set
{
class1List = value.First();
_class1List = value;
}
}
}

Using custom JsonConverter and TypeNameHandling in Json.net

I have a class with an interface-typed property like:
public class Foo
{
public IBar Bar { get; set; }
}
I also have multiple concrete implementations of the IBar interface that can be set at runtime. Some of these concrete classes require a custom JsonConverter for serialization & deserialization.
Utilizing the TypeNameHandling.Auto option the non-convertor requiring IBar classes can be serialized and deserialized perfectly. The custom-serialized classes on the other hand have no $type name output and while they are serialized as expected, they cannot be deserialized to their concrete type.
I attempted to write-out the $type name metadata myself within the custom JsonConverter; however, on deserialization the converter is then being bypassed entirely.
Is there a workaround or proper way of handling such a situation?
I solved the similar problem and I found a solution. It's not very elegant and I think there should be a better way, but at least it works. So my idea was to have JsonConverter per each type that implements IBar and one converter for IBar itself.
So let's start from models:
public interface IBar { }
public class BarA : IBar { }
public class Foo
{
public IBar Bar { get; set; }
}
Now let's create converter for IBar. It will be used only when deserializing JSON. It will try to read $type variable and call converter for implementing type:
public class BarConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotSupportedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jObj = JObject.Load(reader);
var type = jObj.Value<string>("$type");
if (type == GetTypeString<BarA>())
{
return new BarAJsonConverter().ReadJson(reader, objectType, jObj, serializer);
}
// Other implementations if IBar
throw new NotSupportedException();
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof (IBar);
}
public override bool CanWrite
{
get { return false; }
}
private string GetTypeString<T>()
{
var typeOfT = typeof (T);
return string.Format("{0}, {1}", typeOfT.FullName, typeOfT.Assembly.GetName().Name);
}
}
And this is converter for BarA class:
public class BarAJsonConverter : BarBaseJsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// '$type' property will be added because used serializer has TypeNameHandling = TypeNameHandling.Objects
GetSerializer().Serialize(writer, value);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var existingJObj = existingValue as JObject;
if (existingJObj != null)
{
return existingJObj.ToObject<BarA>(GetSerializer());
}
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(BarA);
}
}
You may notice that it's inherited from BarBaseJsonConverter class, not JsonConverter. And also we do not use serializer parameter in WriteJson and ReadJson methods. There is a problem with using serializer parameter inside custom converters. You can read more here. We need to create new instance of JsonSerializer and base class is a good candidate for that:
public abstract class BarBaseJsonConverter : JsonConverter
{
public JsonSerializer GetSerializer()
{
var serializerSettings = JsonHelper.DefaultSerializerSettings;
serializerSettings.TypeNameHandling = TypeNameHandling.Objects;
var converters = serializerSettings.Converters != null
? serializerSettings.Converters.ToList()
: new List<JsonConverter>();
var thisConverter = converters.FirstOrDefault(x => x.GetType() == GetType());
if (thisConverter != null)
{
converters.Remove(thisConverter);
}
serializerSettings.Converters = converters;
return JsonSerializer.Create(serializerSettings);
}
}
JsonHelper is just a class to create JsonSerializerSettings:
public static class JsonHelper
{
public static JsonSerializerSettings DefaultSerializerSettings
{
get
{
return new JsonSerializerSettings
{
Converters = new JsonConverter[] { new BarConverter(), new BarAJsonConverter() }
};
}
}
}
Now it will work and you still can use your custom converters for both serialization and deserialization:
var obj = new Foo { Bar = new BarA() };
var json = JsonConvert.SerializeObject(obj, JsonHelper.DefaultSerializerSettings);
var dObj = JsonConvert.DeserializeObject<Foo>(json, JsonHelper.DefaultSerializerSettings);
Using information from Alesandr Ivanov's answer above, I created a generic WrappedJsonConverter<T> class that wraps (and unwraps) concrete classes requiring a converter using a $wrappedType metadata property that follows the same type name serialization as the standard $type.
The WrappedJsonConverter<T> is added as a converter to the Interface (ie. IBar), but otherwise this wrapper is completely transparent to classes that do not require a converter and also requires no changes to the wrapped converters.
I used a slightly different hack to get around the converter/serializer looping (static fields), but it does not require any knowledge of the serializer settings being used, and allows for the IBar object graph to have child IBar properties.
For wrapped objects the Json looks like:
"IBarProperty" : {
"$wrappedType" : "Namespace.ConcreteBar, Namespace",
"$wrappedValue" : {
"ConvertedID" : 90,
"ConvertedPropID" : 70
...
}
}
The full gist can be found here.
public class WrappedJsonConverter<T> : JsonConverter<T> where T : class
{
[ThreadStatic]
private static bool _canWrite = true;
[ThreadStatic]
private static bool _canRead = true;
public override bool CanWrite
{
get
{
if (_canWrite)
return true;
_canWrite = true;
return false;
}
}
public override bool CanRead
{
get
{
if (_canRead)
return true;
_canRead = true;
return false;
}
}
public override T ReadJson(JsonReader reader, T existingValue, JsonSerializer serializer)
{
var jsonObject = JObject.Load(reader);
JToken token;
T value;
if (!jsonObject.TryGetValue("$wrappedType", out token))
{
//The static _canRead is a terrible hack to get around the serialization loop...
_canRead = false;
value = jsonObject.ToObject<T>(serializer);
_canRead = true;
return value;
}
var typeName = jsonObject.GetValue("$wrappedType").Value<string>();
var type = JsonExtensions.GetTypeFromJsonTypeName(typeName, serializer.Binder);
var converter = serializer.Converters.FirstOrDefault(c => c.CanConvert(type) && c.CanRead);
var wrappedObjectReader = jsonObject.GetValue("$wrappedValue").CreateReader();
wrappedObjectReader.Read();
if (converter == null)
{
_canRead = false;
value = (T)serializer.Deserialize(wrappedObjectReader, type);
_canRead = true;
}
else
{
value = (T)converter.ReadJson(wrappedObjectReader, type, existingValue, serializer);
}
return value;
}
public override void WriteJson(JsonWriter writer, T value, JsonSerializer serializer)
{
var type = value.GetType();
var converter = serializer.Converters.FirstOrDefault(c => c.CanConvert(type) && c.CanWrite);
if (converter == null)
{
//This is a terrible hack to get around the serialization loop...
_canWrite = false;
serializer.Serialize(writer, value, type);
_canWrite = true;
return;
}
writer.WriteStartObject();
{
writer.WritePropertyName("$wrappedType");
writer.WriteValue(type.GetJsonSimpleTypeName());
writer.WritePropertyName("$wrappedValue");
converter.WriteJson(writer, value, serializer);
}
writer.WriteEndObject();
}
}

How to remove a property during WriteJson

I have created a test console application that has a simple class named Other. As an exercise, I want to set the OtherString property to null when it is serialized. I know how I could do this with a custom ContractResolver. I need to be able to do this with a custom Converter too.
The first converter I wrote was simple and the way I thought it should be. However, it would throw a "Self referencing loop detected with type 'JsonContractandConvert.Models.Other'. Path ''." exception. After doing some reading, I made some changes and now I have a working converter. These changes are a lot more verbose, but work.
My question is why are these changes required and is there a better way to do this with a Converter?
Other Class:
[JsonConverter(typeof(OtherConverter))]
public class Other
{
public int Id { get; set; }
public string OtherString { get; set; }
public int OtherInt { get; set; }
public string OtherName
{
get
{
return "Other Name = " + this.OtherString;
}
}
}
First Attempt: (This one throws an exception)
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (!this.CanConvert(value.GetType())) return;
var entity = value as Other;
if (entity == null) return;
entity.OtherString = null;
serializer.Serialize(writer, entity);
}
Second Attempt: (This works as expected)
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (!this.CanConvert(value.GetType())) return;
var entity = value as Other;
if (entity == null) return;
entity.OtherString = null;
writer.WriteStartObject();
var props = entity.GetType().GetProperties();
foreach (var propertyInfo in props)
{
var ignorAttribute =
propertyInfo.CustomAttributes.FirstOrDefault(i => i.AttributeType == typeof(JsonIgnoreAttribute));
if (ignorAttribute != null) continue;
var tempVal = propertyInfo.GetValue(entity);
if (tempVal == null) continue;
writer.WritePropertyName(propertyInfo.Name);
serializer.Serialize(writer, tempVal);
}
}
writer.WriteEndObject();
}
Edit:
Here's the code from the console app that I'm using to test.
class Program
{
static void Main(string[] args)
{
var otherObj = new Other { Id = 123, OtherInt = 456, OtherString = "This is the other string"};
var json = JsonConvert.SerializeObject(otherObj, Formatting.Indented);
Console.WriteLine(json);
}
}
Answering my own questions... (I think I've finally got my head around Json Converters)
To answer my basic question as to why one way works and the other doesn't. I think the answer is just that the way that doesn't work is just wrong. From what I can tell, you need to use the writer object. If you don't it won't work. (There may be scenarios where this is not the case, but I never found one.)
If you merely want to remove a property from the Json, use [JsonIgnore]. (Simple right?)
If you want to change the value some how, you'll need to use a Converter. By using a Converter, you're taking responsibility for serializing what ever object the Converter handles. So if the object is a simple type like a String, or an Int it's really easy. If it's something more complex like an array or complex object, then it takes a little more planning.
Here's an example of a simple String Converter:
Class to serialize:
public class Account
{
public int Id { get; set; }
public string AccountName { get; set; }
[JsonIgnore]
public virtual Account DefaultAssignTo { get; set; }
public int? DefaultAssignToId { get; set; }
[JsonIgnore]
public virtual ICollection<Role> Roles { get; set; }
[JsonIgnore]
public virtual Other Other { get; set; }
public int? OtherId { get; set; }
[JsonConverter(typeof(StringConverter))]
public string OtherName
{
get
{
return "Name = " + this.AccountName;
}
}
}
Converter:
public class StringConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(string).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)
{
if (!this.CanConvert(value.GetType())) return;
writer.WriteValue("blah blah blah");
}
}
Run: (As you can see I'm using Entity Framework for my data)
using (var db = new Context())
{
var json = JsonConvert.SerializeObject(db.Accounts.FirstOrDefault(), Formatting.Indented,
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
Console.WriteLine(json);
}
Returns:
{
"Id": 43,
"AccountName": "John",
"DefaultAssignToId": 43,
"OtherId": 19,
"OtherName": "blah blah blah"
}
One of the things that messed me up for a bit was when to use writer.WriteStartObject(). Basically if you are Converting a complex object, you need to use it. If you do, then you need to create all of the property names and values. In the OP you can see an example of how I did this. The biggest down side is any Json attributes that properties are decorated with don't automatically happen. So if you can, decorate the class and let the serializer deal with it. Here's an interesting problem you can run into if you use it wrong.
In the String converter replace:
writer.WriteValue("blah blah blah");
with:
writer.WriteStartObject();
writer.WritePropertyName("BlahProp");
serializer.Serialize(writer, "blah blah blah");
writer.WriteEndObject();
run it again and here's the output: (Notice how OtherName is an object now instead of a string)
{
"Id": 43,
"AccountName": "John",
"DefaultAssignToId": 43,
"OtherId": 19,
"OtherName": {
"BlahProp": "blah blah blah"
}
}
The Roles collection also needed a Converter. Two things to note are first, you don't need to specify the property name and second, any Json attributes that the Role class is decorated with will work as expected.
using BaseCollection = System.Collections.Generic.ICollection<JsonContractandConvert.Models.Role>;
public class RemoveAccountsFromRolesConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(BaseCollection).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)
{
if (!this.CanConvert(value.GetType())) return;
var entities = value as BaseCollection;
if (entities == null) return;
writer.WriteStartArray();
foreach (var entity in entities)
{
entity.Accounts = null;
serializer.Serialize(writer, entity);
}
writer.WriteEndArray();
}
}
Lastly, here's a couple of converters I created for my real project:
This one converts an object to a shallow copy. This is useful if you have a collection where what that collection contains could cause a self referencing loop.
public class ShallowCopyCollectionConverter<TCollectionType, TCopyType> : JsonConverter
where TCollectionType : IEnumerable<TbdEntity>
where TCopyType : TbdEntity, new()
{
public override bool CanConvert(Type objectType)
{
return typeof(TCollectionType).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)
{
if (this.CanConvert(value.GetType()) == false) return;
var entities = (TCollectionType)value;
writer.WriteStartArray();
foreach (var entity in entities)
{
serializer.Serialize(writer, entity.ShallowCopy<TCopyType>()); //ShallowCopy<> is a method in the base class that all of my classes extend.
}
writer.WriteEndArray();
}
}
This one will convert an abstract using the concrete type:
public class DataSnapInConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(DataSnapIn) == (objectType);
}
public override bool CanWrite
{
get { return false; }
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (this.CanConvert(objectType) == false) return null;
var jo = JObject.Load(reader);
var typeName = jo["snapInType"] ?? jo["SnapInType"]; //the abstract classes have this property to identify what concrete class they are.
var typeNameString = typeName.ToString();
var deserializeType = Type.GetType(typeNameString);
if(deserializeType == null)
throw new Exception("SnapInType is null or does not reference a valid class.");
var result = Activator.CreateInstance(deserializeType);
serializer.Populate(jo.CreateReader(), result);
return result;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}

Categories

Resources