2

There is some data that I receive from a service in json format. A part of the data is an object of class BASE (say). There are many classes that derive from it: enter image description here

Now the server sends a fixed structure json, and an object of type BASE lies in it. Some sample responses I am listing down here:

"{"Status":"Success","B":{"$type":"B1","id":"123"},"C":null}"

"{"Status":"Success","A":{"$type":"A3","name":"Jon"},"B":{"$type":"B2","id":"A34J"}}"

I have the same class hierarchy defined in my client-side Models and I am using NewtonSoft Json Library to deserialize the content. As you can see, the server is sending the $type information of the every object that's derived from BASE class, I want to be able to load those objects during client-side deserialization into their respective type. I deserliaze the json into an object of type Response

class Response
{
   string Status;
   BASE object1;
   BASE object2;
}

Response resp = JsonConvert.DeserializeObject<Response>( jsonServiceMsg, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto, Binder = mycustombinder });

As you can see, I am not specifying the exact type of object B1 or A3 or B2, in the Response object, I want the Newtonsoft lib to init the object1 and object2, with appropriate type based on $type value in the json message.

Note that the Service has these classes (Base and all derivetives) defined in a different namespace and I (client) in different namespace. But for simplicity the service is not binding the namespace by using a custom binder while serializing into a json response.

I know I can achieve this by defining a custom binder (class derived from SerializationBinder) and overriding the BindToName and BindToType properties (refer link)

But the issue is that we do not want to use Json.Net SerializationBinder from System.Runtime.Serialization. Our module can only use Newtonsoft lib. Any solutions to this?

Community
  • 1
  • 1
Rajat
  • 53
  • 2
  • 9
  • 1
    You could create a custom `JsonConverter` for `Base` along the lines of [Deserializing polymorphic json classes without type information using json.net](https://stackoverflow.com/questions/19307752). Even though, in this case, you do have type information, you would process it manually in the converter along the lines of that answer. – dbc Jun 13 '16 at 13:43
  • The json I am receiving is pretty big and the two Base type objects are just one part of it. Don't you think i'll have to write too much code if i go by custom JsonConverter. I have to serialize it as well. Wouldn't that be too much code? – Rajat Jun 13 '16 at 15:28
  • The `JsonConverter` would be for `Base` and its abstract subclasses. All you would need to do is to maintain a lookup dictionary from type name to type. The overall size of the JSON is irrelevant and the number of other classes is irrelevant. Certainly it would be even easier to write your own `SerializationBinder`, that is the intended way for this problem to be solved - but for whatever reason you don't want to do that. – dbc Jun 13 '16 at 16:20
  • Pleas see the answer. Is that what you are suggesting. – Rajat Jun 14 '16 at 12:25

1 Answers1

0

As dbc suggested to write a custom JsonConverter, as used here

I think it'll go like this:

public override object ReadJson(JsonReader reader, 
        Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject item = JObject.Load(reader);
        switch (item[$type].Value<string>())
        {
            case "B1":
                return item.ToObject<B1>();
            case "B2":
                return item.ToObject<B2>();
            case "C1":
                return item.ToObject<C1>();
            case "A1":
                return item.ToObject<A1>();
            case "A2":
                return item.ToObject<A2>();
            case "A3":
                return item.ToObject<A3>();
        }
    }

Similarly I'll have to write the WriteJson method of the custom JsonConverter. Is that what you mean dbc?

If Yes I have few questions:

  1. How performent is this code? The JObject.Load(reader) seems expensive for every call?
  2. Will it work fine if BASE type object is nested deep inside the json?
Rajat
  • 53
  • 2
  • 9
  • 1) You will have to [test it yourself](https://ericlippert.com/2012/12/17/performance-rant/). 2) Yes, it doesn't matter how deeply the BASE object is nested. If you are having some problems you can update your question with the details. – dbc Jun 14 '16 at 14:28
  • That worked well. I listed down all child types in the switch-case and return ed their objects. But what about WriteJson? I want to be able to serialize this object back to a string with $type property preserved for polymorphic types. How do I write WriteJson such that only for polymorphic types, it write the $type property as well in the Json. "{"Status":"Success","A":{"$type":"A3","name":"Jon"},"B":{"$type":"B2","id":"A34J"}}" – Rajat Jun 15 '16 at 08:09