【问题标题】:Newtonsoft Json deserialization into specific typesNewtonsoft Json 反序列化为特定类型
【发布时间】:2017-03-11 14:23:56
【问题描述】:

我有一个 Action 类和许多其他从该类派生的特定类,例如 SendCoordinates、MakeCall 等。

所以给定一个像这样的 JSON 响应:

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

现在我的问题是关于 Newtonsoft json 库。实现这一点的最佳方法是什么?我是否应该像这里显示的那样为每个类创建特定的 JSON 转换器http://www.newtonsoft.com/json/help/html/DeserializeCustomCreationConverter.htm

或者我应该采用我没有考虑的完全不同的方法吗?各位能不能给我留下你的意见?提前致谢

【问题讨论】:

  • 这不是有效的 JSON...
  • action 数组还是对象?
  • @GeneR 这是一个对象,现在有效,对不起!

标签: c# json json.net json-deserialization


【解决方案1】:

使用 Newtonsoft.Json,您可以通过非泛型重载 DeserializeObject(string value, type type) 反序列化为类型。

这意味着您可以使用Type 属性作为要反序列化的类型的提示。

  1. 反序列化为基本类型
  2. 获取实际Action对象的类型名称
  3. 获取类型全名
  4. 反序列化为派生动作类型

请看下面的例子:

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);
        }

    }
}

如果您不想在 Type 字段中指定完整的限定类型名称,则可以使用自定义程序逻辑构造该类型的 FullName(例如,使用基本命名空间作为前缀)。

编辑:根据 cmets,它应该能够反序列化(派生类型的)动作的列表。因此,我将以下示例附加到我的答案中,您可以在其中看到如何通过执行以下操作来反序列化操作列表:

  1. 反序列化为通用 JArray
  2. 为数组中的每个项目确定TypeAction
  3. 反序列化为特定的派生类型

我还在转换后添加了一个循环,以展示如何进一步处理转换后的操作。

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;
                }
            }

        }

    }
}

【讨论】:

  • 感谢您的回复。所以这必须是两步反序列化?鉴于您的示例,我希望“var action”已经是 Action1 类型。有没有办法做到这一点?
  • 您当然可以诉诸文本解析(或正则表达式)json 文本来找出type 属性。但是这种方法更安全且易于排除故障(当添加了一些错误检查时)。如果你真的想避免声明另一个变量,你可以像这样使它更紧凑:var action1 = (Action1) JsonConvert.DeserializeObject(ACTION1, Type.GetType(JsonConvert.DeserializeObject&lt;Action&gt;(ACTION1).Type));,但这不会使它更具可读性。有什么不使用两个变量的原因吗?
  • 是的,因为我的主要 JSON 响应将是一个动作数组,我将根据该信息填充一个视图,如下所示:` var content = JsonConvert.DeserializeObject(Actions); ` 那么有没有办法为 JsonConvert 提供他需要的工具来使这种特定的转换自动化?
  • 这也不是问题:反序列化为List&lt;Action&gt;。从那里您可以遍历列表并随后反序列化为相应的派生操作类型
  • 谢谢!!你想更新你的答案让我把它标记为正确吗?
【解决方案2】:

您可以将其反序列化为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 { ... }

可能json 反序列化:

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

或:

{
  "call":{ ... }
}

【讨论】:

  • 公开 SomeAction 动作 ?这对我来说没有意义。行动是我的基类。该字段不应该只是 public Action 吗?
  • 我的问题是如何决定我应该转换成哪种类型
  • 那么你的json应该看起来像{ "type":"SendCoordinates", "data": "41°24'12.2 N 2°10'26.5" },然后你可以反序列化到你的Action class
  • 以及如何反序列化到我的特定类?
  • var action = JsonConvert.DeserializeObject&lt;SomeAction&gt;(json)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多