【问题标题】:Remove nesting/arrays when deserializing JSON using JSON.Net使用 JSON.Net 反序列化 JSON 时删除嵌套/数组
【发布时间】:2017-05-09 18:45:55
【问题描述】:

我在尝试从我正在使用的 API 中反序列化一些 JSON 时遇到了一些问题。出于某种原因,API 喜欢在不需要时将数据包装在额外的层和数组中。

{
    "CustomData": [
        {
            "Wrapper": [
                {
                    "OptionalDataSet1": [
                        {
                            "ItemA": "Basic string"
                        },
                        {
                            "ItemB": "Another basic string"
                        }
                    ]
                }
            ]
        }
    ]
}

使用 json2csharp,我得到了适用于上述示例的类。

public class OptionalDataSet1
{
    public string ItemA { get; set; }
    public string ItemB { get; set; }
}

public class Wrapper
{
    public List<OptionalDataSet1> OptionalDataSet1 { get; set; }
}

public class CustomData
{
    public List<Wrapper> Wrapper { get; set; }
}

public class RootObject
{
    public List<CustomData> CustomData { get; set; }
}

我遇到的问题是根据 API 不需要“Wrapper”类。它始终存在,但将是自定义数据中的唯一项目。此外,Wrapper 内只会有一个“OptionalDataSet1”实例。还有其他“可选数据集”,但同样,它们在每个请求中都是唯一的。

最后,Wrapper 为“OptionalDataSet1”反序列化两个对象,第一个带有 ItemA 的值,第二个带有 ItemBs。还有其他数据集可以有超过 40 个可用的项目,我不想扫描一个对象的 40 个实例来查找哪个具有我要查找的单个数据属性。

我是否应该在通过删除“包装器”并将 List 属性转换为单个实例来发送它以反序列化之前从 API 接收到的 JSON 字符串,或者我是否缺少另一种方法JSON.Net 产生类似的东西

RootObject.CustomData.OptionalDataSet1.ItemB

代替

RootObject.CustomData[0].Wrapper[0].OptionalDataSet[1].ItemB

【问题讨论】:

  • 试试这个解决方案,但我不知道我是否理解你的问题:stackoverflow.com/questions/30984416/…
  • 你能发布一个你的外部 API json 的样例吗?我认为你发布的 json 是你正在生成的
  • 您有两个不同的问题:1) 如何将嵌套的 JSON 反序列化为平面类。 2)如何根据一些过滤器有选择地反序列化数组项。对于 1) 您可以查看 Can I serialize nested properties to my class in one operation with Json.net?Can I specify a path in an attribute to map a property in my class to a child property in my JSON?。 2) 对于"OptionalDataSet1",考虑使用List&lt;Dictionary&lt;string, string&gt;&gt;
  • JSON 示例正是外部 API 返回的内容,只是对敏感数据进行了清理。不过,我会查看其他答案,并希望自定义转换器将成为我需要的解决方案。
  • @dbc 您包含的链接之一最终将成为我所追求的解决方案。我只是在编写一个自定义的 JsonConverter,它在创建 JToken 后采用 .First 元素。粗鲁,但它有效,我不必弄乱 API 返回的 Json。

标签: c# json json.net


【解决方案1】:

听起来您真的只对 JSON 中最内层数组 (OptionalDataSet1) 中的键值对感兴趣,而这些键可能会因您从 API 请求的数据集而异。在这种情况下,我将使用LINQ-to-JSON API 创建一个辅助方法来解析JSON 并在Dictionary&lt;string, string&gt; 中返回所需的数据。那么您就不必担心为所有不同的数据集定义不同的类了。

public static Dictionary<string, string> Deserialize(string json)
{
    return JObject.Parse(json)
        .SelectToken("CustomData[0].Wrapper[0].OptionalDataSet1")
        .Children<JObject>()
        .SelectMany(jo => jo.Properties())
        .ToDictionary(jp => jp.Name, jp => (string)jp.Value);
}

然后你可以像这样反序列化:

Dictionary<string, string> optionalDataSet1 = Deserialize(json);

从那里您可以轻松地直接访问您感兴趣的任何项目:

string itemA = optionalDataSet1["ItemA"];

或者您可以像这样转储所有键值对:

foreach (var kvp in optionalDataSet1)
{
    Console.WriteLine(kvp.Key + ": " + kvp.Value);
}

小提琴:https://dotnetfiddle.net/6ekOFp

【讨论】:

    【解决方案2】:

    好吧,虽然我非常分心,因为您确信 API 将始终在数组中返回 1 个实例,但我会回答应该这样做,而不是破解 json。

    主要原因是将您的实现从实际数据对象 (DTO) 中抽象出来。 因此,对于您生成的类 - 就像这样离开它们。它是你的 API 的传输协议,而不是你的业务逻辑范围,所以你最好不要以任何方式接触它。相信我,协议的每一次更改都会触及您的业务部分——这不好,每次有人想在前端更改 API 时都会导致开发人员不必要的时间消耗。

    它只是生成的代理类,它可以很容易地重新生成,你不​​能改变它们。 API 可以更改,您的代码应该是完整的。

    不用这种技巧,只需将它们映射到您想要的任何结构中:

    public class MyApiResponse
    {
         public string ItemA {get;set;}
         public string ItemB {get;set;}
    }
    
    var n = new MyApiResponse
    {
        ItemA = ...,
        ItemB = ...
    }
    

    【讨论】:

      【解决方案3】:

      好吧,最终解决方案是编写我自己的 JsonConverter,如 dbc 所述

      public override object ReadJson(JsonReader reader, Type objectType, 
                                      object existingValue, JsonSerializer serializer)
      {
          JObject jo = JObject.Load(reader);
          object targetObj = Activator.CreateInstance(objectType);
      
          foreach (PropertyInfo prop in objectType.GetProperties()) {
              string jsonPath = prop.Name;
              JToken token = jo.SelectToken(jsonPath);
      
              if (jsonPath.Equals("OptionalDataSet1")) {
                  var innerItems = jo.SelectToken("Wrapper[0]").SelectToken(jsonPath).Values();
                  var finalItem = new JObject();
                  foreach (var item in innerItems) {
                      var tempItem = new JObject(item);
                      finalItem.Merge(tempItem);
                  }
              } else {
                  token = jo.SelectToken(jsonPath).First();
              }
      
              if (token != null && token.Type != JTokenType.Null) {
                  object value = token.ToObject(prop.PropertyType, serializer);
                  prop.SetValue(targetObj, value, null);
              }
          }
      
          return targetObj;
      }
      

      使用此自定义转换器,我能够消除对 Wrapper 类的需求,并拥有 OptionalDataSet1 和 CustomData 的单个实例,并且 OptionalDataSet1 的单个实例正确填充了所有信息。

      (代码可能已关闭,为此答案从 VB.Net 动态转换)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-01-17
        • 2019-09-22
        • 2023-03-22
        • 1970-01-01
        • 2014-02-19
        • 2011-09-18
        相关资源
        最近更新 更多