简体   繁体   中英

Newtonsoft Json deserialization into specific types

I have a class Action and many other specific classes that derive from this one like SendCoordinates, MakeCall, etc..

So given a JSON response like this one:

 {
      "action":{
          "type":"SendCoordinates",
          "data": "41°24'12.2 N 2°10'26.5"
       }
}

Now my question is regarding Newtonsoft json library. What is the best way to implement this ? Should I create specific JSON converters for each class like they show here http://www.newtonsoft.com/json/help/html/DeserializeCustomCreationConverter.htm

Or should I go for a entirelly different aproach that I'm not considering ? Can you guys leave me your opinions on this ? Thanks in advance

With Newtonsoft.Json you can deserialise to a type via the non-generic overload DeserializeObject(string value, type type) .

This means you can use the Type property as a hint to which type to deserialize.

  1. deserialise to base type
  2. get type name of actual Action object
  3. get type of type full name
  4. deserialise to derived action type

See the following example:

using System;
using Newtonsoft.Json;

namespace com.example.SO42736347
{
    public class Action
    {
        public string Type { get; set; }
    }

    public class Action1 : Action
    {
        public string Data { get; set; }
    }

    public class Program
    {

        public const string ACTION1 = @"{
            ""Type"" : ""com.example.Json.Action1"",
            ""Data"" : ""41°24'12.2 N 2°10'26.5""
        }";

        public static void Main()
        {
            var action = JsonConvert.DeserializeObject<Action>(ACTION1);

            var type = Type.GetType(action.Type);

            var action1 = (Action1) JsonConvert.DeserializeObject(ACTION1, type);
        }

    }
}

If you do not want to specify the full qualified type name in the Type field, you can construct the FullName of the type by using custom programme logic (eg prefixing it with a base namespace).

Edit : according to the comments it should be able to deserialise a list of actions (of derived types). Therefore I appended the following example to my answer where you can see, how to deserialise a list of actions by doing the following:

  1. deserialise to generic JArray
  2. for each item in array determine the Type of the Action
  3. deserialise into the specific derived type

I also added a loop after the conversion to show how to further process the converted actions.

using System;
using Newtonsoft.Json;
using System.Collections.Generic; 
using Newtonsoft.Json.Linq;

namespace com.example.Json
{
    public class Action
    {
        public string Type { get; set; }
    }

    public class Action1 : Action
    {
        public string Data { get; set; }
    }

    public class Action2 : Action
    {
        public string SomeProperty { get; set; }
    }

    public class Program
    {

        public const string ACTION1 = @"{
        ""Type"" : ""com.example.Json.Action1"",
            ""Data"" : ""41°24'12.2 N 2°10'26.5""
        }";

        public const string ACTION2 = @"{
        ""Type"" : ""com.example.Json.Action2"",
            ""SomeProperty"" : ""arbitrary-value""
        }";

        public const string ACTIONS =  
            "[" + 
            ACTION1 +
            "," + 
            ACTION2 +
            "]" ;

        public static void Main()
        {

            var actions = new List<Action>();

            JArray jArray = JArray.Parse(ACTIONS);
            foreach(var item in jArray)
            {
                var json = JsonConvert.SerializeObject(item);
                var baseAction = JsonConvert.DeserializeObject<Action>(json);
                var type = Type.GetType(baseAction.Type);
                var action = (Action) JsonConvert.DeserializeObject(json, type);
                actions.Add(action);
            }

            // now that we have converted all array items into specific derived action objects
            // we can start processing them anyway we want
            // keep in mind that you have to check the runtime type in order to find out what
            // specific kind of action we have

            // eg.
            foreach(var action in actions)
            {
                switch(action.Type)
                {
                    case "com.example.Json.Action1":
                        // do something
                        Console.WriteLine("found com.example.Json.Action1");
                        Console.WriteLine((action as Action1).Data);
                        break;
                    case "com.example.Json.Action2":
                        // do something
                        Console.WriteLine("found com.example.Json.Action2");
                        Console.WriteLine((action as Action2).SomeProperty);
                        break;
                    default:
                        // do something
                        Console.WriteLine("found something else");
                        break;
                }
            }

        }

    }
}

You can deserialize it to SomeObject :

public class SomeObject
{
  public SomeAction Action { get; set; }
  public OtherAction Call { get; set; }
}
public class SomeAction
{
  public string Type { get; set; }
  public string Data { get; set; }
}
public class OtherAction { ... }

Possible json to deserialize:

{
  "action":{ "type":"SendCoordinates", "data": "41°24'12.2 N 2°10'26.5" }
}

or:

{
  "call":{ ... }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM