【问题标题】:Is there a generic way to access fields within objects within arrays in JSON where the schema is dynamic?是否有一种通用的方法来访问模式是动态的 JSON 数组中的对象内的字段?
【发布时间】:2019-04-04 14:38:36
【问题描述】:

我希望能够获取/设置a 的属性值:

{
   "payload":[
      {
         "a":"yes",
         "b":"no",
         "c":"maybe"
      },
      {
         "a1":"agg",
         "b":"no",
         "c":"maybe"
      },
      {
         "a":"L",
         "b":"k",
         "c":"maybe"
      }
   ]
}

我想遍历所有 a 并获取/设置其值。在伪代码中是这样的:

foreach(var jsonObject in payload)
{
  jsonObject.a = "ZZZZZZZZ";
}

生成的结构会是这样的:

{
   "payload":[
      {
         "a":"ZZZZZZZZ",
         "b":"no",
         "c":"maybe"
      },
      {
         "a1":"agg",
         "b":"no",
         "c":"maybe"
      },
      {
         "a":"ZZZZZZZZ",
         "b":"k",
         "c":"maybe"
      }
   ]
}

我们如何访问数组中对象的值?

请注意架构是动态的。

【问题讨论】:

  • 查看System.Reflection 中的调用。您需要反映每个对象(通过在对象上调用GetType()),获取对象的字段,如果字段名为a 并且类型为string,则设置其值“ZZZZZZ”
  • 这在 json.net 中没有吗?
  • 是,索引器操作符(在 JObject 类中)

标签: c# .net json json.net


【解决方案1】:

使用 JSON.NET 有多种选择。根据我对您的用例的理解,我可能会选择这样的一个:

var jObject = JsonConvert.DeserializeObject<JObject>(json);
foreach (var jsonObject in jObject["payload"])
{
    if (jsonObject["a"] != null)
    {
        jsonObject["a"] = "ZZZZZZZZ";
    }
}

这种方法的一个主要优点是JObject 可以转换回 JSON(例如,如果您的代码的目的只是进行此更改并再次保存 JSON)。 Here's a LINQPad script 显示此工作。

其他选项包括:

  • 反序列化为dynamic,所以你可以说obj.payload而不是obj["payload"]
  • 反序列化为静态类型(因为架构是动态的,这听起来对您来说不是入门)
  • 使用SelectTokens() 为要更改的元素提供 JsonPath。如果目标对象位于 JSON 有效负载的深处而不是顶层,这将很有用。

【讨论】:

  • 非常感谢,无关的问题,有没有不同的方式来表达JsonConvert.DeserializeObject&lt;JObject&gt;(json); ?我们可以简单地做JsonConvert.DeserializeObject(json); 吗?为什么我们需要指定JObject
  • @RistoM:很好。我修正了我的答案以适应这一点。
  • @l--''''''---------'''''''''''': DeserializeObject 方法被设计用于各种目的。您可以使用它反序列化为 dynamicExpandoObject 或您选择的静态类型。库中没有假设您需要该特定类型的重载。如果您发现自己经常这样做,您可以编写自己的实用程序方法。
  • 知道了!非常感谢您提供 linqpad 脚本!运行良好。据我了解,不可能以允许我们引用jsonObject["a"] 的方式使用SelectTokens
  • @l--''''''---------'''''''''''' 是的,这是可能的。有关示例,请参见 my answer
【解决方案2】:

以@StriplingWarrior 的answer 中的第三个子弹为例:

您可以使用 SelectTokens()JsonPath 通配符表达式来查找 JSON 中的所有匹配属性,然后对其进行修改。

JToken root = JToken.Parse(json);
foreach (JToken match in root.SelectTokens("payload[*].a"))
{
    match.Replace(new JValue("ZZZZZZZZ"));
}

小提琴:https://dotnetfiddle.net/ZYIchL

【讨论】:

  • 既然您在JToken 上执行.Replace(),这是否意味着我们不能这样做:match.Replace(new JArray(new JValue("ZZZZZZZZ")))??我们只能将“原始”值放在JToken 中吗?
  • Replace 接受JToken,可以是JArrayJObjectJValueJToken 是所有这些的基类)。所以yes,你可以做match.Replace(new JArray(new JValue("ZZZZZZZZ")))
  • 有关 JTokens 如何关联的更多信息,请参阅 this answer
  • 谢谢你添加这个,布赖恩。
【解决方案3】:

这是一种具有 dto 类和 Json.NET 反序列化/序列化的解决方案。此解决方案假设 json 架构是固定的。

    using Newtonsoft.Json;
    using System.Collections.Generic;
    class Program
    {
        static void Main(string[] args)
        {
            var json = @"{
               ""payload"":[
                  {
                     ""a"":""yes"",
                     ""b"":""no"",
                     ""c"":""maybe""
                  },
                  {
                     ""a1"":""agg"",
                     ""b"":""no"",
                     ""c"":""maybe""
                  },
                  {
                     ""a"":""L"",
                     ""b"":""k"",
                     ""c"":""maybe""
                  }
               ]
            }";

            var o= JsonConvert.DeserializeObject<RootObject>(json);
            foreach (var item in o.Payload)
            {
                item.A = "ZZZZZZZZ";
            }
            var convertedJson = JsonConvert.SerializeObject(o);


        }
    }

    public class RootObject
    {
        [JsonProperty("payload")]
        public List<Payload> Payload { get; set; }
    }

    public class Payload
    {
        [JsonProperty("a", NullValueHandling = NullValueHandling.Ignore)]
        public string A { get; set; }

        [JsonProperty("b")]
        public string B { get; set; }

        [JsonProperty("c")]
        public string C { get; set; }

        [JsonProperty("a1", NullValueHandling = NullValueHandling.Ignore)]
        public string A1 { get; set; }
    }

【讨论】:

  • 非常感谢这是一个很好的解决方案,不幸的是我忘记分享的一个重要信息是架构是动态的
  • 好的,我明白了.. :) 然后通用 JObject 或 C# 动态就是答案。如果没有其他正确答案,我稍后会修改我的答案..
猜你喜欢
  • 1970-01-01
  • 2014-05-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-11
  • 2013-04-21
相关资源
最近更新 更多