【问题标题】:How do I parse a JSON object in C# when I don't know the key in advance?事先不知道密钥的情况下,如何在 C# 中解析 JSON 对象?
【发布时间】:2012-01-13 18:38:30
【问题描述】:

我有一些如下所示的 JSON 数据:

{
  "910719": {
    "id": 910719,
    "type": "asdf",
    "ref_id": 7568
  },
  "910721": {
    "id": 910721,
    "type": "asdf",
    "ref_id": 7568
  },
  "910723": {
    "id": 910723,
    "type": "asdf",
    "ref_id": 7568
  }
}

如何使用 JSON.net 解析它?我可以先这样做:

JObject jFoo = JObject.Parse(data);

我需要能够遍历此列表中的每个对象。我希望能够做这样的事情:

foreach (string ref_id in (string)jFoo["ref_id"]) {...}

foreach (JToken t in jFoo.Descendants())
{
    Console.WriteLine((string)t["ref_id"]);
}

但这当然行不通。如果您在编写代码时知道密钥,那么所有示例都非常有用。当您事先不知道密钥时,它就会崩溃。

【问题讨论】:

  • 问题..你是想序列化 JSON 对象还是直接根据“ref_id”解析出来
  • 我想要一个 ref_id 列表,以便在另一个请求中使用它们。

标签: c# json json.net


【解决方案1】:

这是可行的;这可行,但并不优雅。我确信有更好的方法。

var o = JObject.Parse(yourJsonString);

foreach (JToken child in o.Children())
{
    foreach (JToken grandChild in child)
    {
        foreach (JToken grandGrandChild in grandChild)
        {
            var property = grandGrandChild as JProperty;

            if (property != null)
            {
                Console.WriteLine(property.Name + ":" + property.Value);
            }
        }
    }
}

打印:

id:910719
类型:asdf
ref_id:7568
编号:910721
类型:asdf
ref_id:7568
编号:910723
类型:asdf
ref_id:7568

【讨论】:

    【解决方案2】:

    您可以像这样使用简单的 LINQ 查询来遍历子对象:

    JObject jFoo = JObject.Parse(json);
    
    foreach (JObject obj in jFoo.Properties().Select(p => p.Value))
    {
        Console.WriteLine("id: " + obj["id"]);
        Console.WriteLine("type: " + obj["type"]);
        Console.WriteLine("ref_id: " + obj["ref_id"]);
    }
    

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

    或者,如果您只想要所有 ref_id 值,您可以执行以下操作:

    string[] refIds = jFoo.Properties()
                          .Select(p => (string)p.Value["ref_id"])
                          .ToArray();
    
    Console.Write(string.Join("\r\n", refIds));
    

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

    【讨论】:

      【解决方案3】:

      我正在使用 Json.NET,我编写了一种快速方法,您可以使用递归方法打印出所有键和相应的值。

            var o = JObject.Parse(YourJsonString);
            getAllProperties(o); //call our recursive method
      

      然后你可以使用这个递归的方法来获取所有的属性和它们的值

         void getAllProperties(JToken children)
          {
              foreach (JToken child in children.Children())
              {
                  var property = child as JProperty;
                  if (property != null)
                  {
                      Console.WriteLine(property.Name + " " + property.Value);//print all of the values
                  }
                  getAllProperties(child);
              }
          }
      

      【讨论】:

        【解决方案4】:

        您是否考虑过使用 JavascriptSerializer?

        你可以尝试做这样的事情:

        JavaScriptSerializer serializer = new JavaScriptSerializer();
        var foo = serializer.Deserialize<Dictionary<string, Dictionary<string, string>>>(data);
        foreach(var item in foo)
        {
            Console.Writeln(item.Value["ref_id"]);
        }
        

        http://msdn.microsoft.com/en-us/library/system.web.script.serialization.javascriptserializer.aspx

        【讨论】:

        • 我宁愿不退出 JSON.net...但如果没有其他问题我会这样做。
        • 您可以在控制台应用程序中使用 javascriptserializer 吗?我在添加对 System.Web.Extensions 的引用时遇到了困难...
        • 是的,您可以,但您需要将其从客户端配置文件应用程序转换为常见的 .Net 框架应用程序。请参阅msdn.microsoft.com/en-us/library/… 以供参考
        【解决方案5】:

        Konstantin 的解决方案会起作用,但如果你想要一个 Id 列表做同样的事情,而不是 Console.Writeln() 使用以下

        List<string> list = new List<string>();
        JavaScriptSerializer serializer = new JavaScriptSerializer();
        var foo = serializer.Deserialize<Dictionary<string, Dictionary<string, string>>>(data);
        foreach(var item in foo)
        {
            list.Add(item.Value["ref_id"]);
        }
        

        【讨论】:

          【解决方案6】:

          我发现 TrueWill 的答案有效,但我想避免使用 foreach 并尝试使用一个简单的 for 循环来提高速度。我的结果肯定是充其量只能说是丑陋的。它们在这里,以防它们对任何人有用。 (我离开 WriteLine 是为了更容易看清事物。)

          请注意,这不适用于某些 JSON,也不是完全通用的。一些空检查可以做得更好,等等。

                 // NOW, DOING IT ALL AS A FOR LOOP...
                  // a, b, c, d - for iterator counters.
                  // j1, j2, j3, j4 - the JTokens to iterator over - each is a child of the previous
                  // p, q, r, s - The properties from j1/2/3/4. 
          
                  JObject o = JObject.Parse(json);
                  JToken j1 = o.First;
                  for (int a = 0; a < o.Children().Count(); a++) { // Outermost loop gives us result, error, id. 
                      if (j1 == null)
                          continue;
                      if (a > 0) {
                          j1 = j1.Next;
                          if (j1 == null)
                              continue;
                      } 
                      var p = j1 as JProperty;
                      Console.WriteLine("FOR 0 = " + a.ToString() + " --- " + p.Name);
                      // DO STUFF HERE.
          
                      // FIRST INNER LOOP
                      // Set up a JToken or continue
                      JToken j2 = j1.Children().First() as JToken;
                      if (j1.Children().Count() > 0) {
                          j2 = j1.Children().First() as JToken;
                      } else {
                          continue;
                      }
                      Console.WriteLine("*** STARTING FIRST INNER...");
                      for (int b = 0; b < j1.Children().Count(); b++) { // returns nothing as second loop above.
                          if (j2 == null) {
                              Console.WriteLine("*** j2 null 1...");
                              continue;
                          }
                          if (b > 0) {
                              j2 = j2.Next;
                              if (j2 == null) {
                                  Console.WriteLine("*** j2 null 2...");
                                  continue;
                              }
                          }
                          var q = j2 as JProperty;
                          // These null checks need to be != or ==, depending on what's needed. 
                          if (q != null) {
                              Console.WriteLine("FOR 1 = " + a.ToString() + ","
                                + b.ToString() + " --- " + q.Name);
                              // DO STUFF HERE.
                              // ...
                          } // q !null check
          
                          // SECOND INNER LOOP
                          // Set up a JToken or continue
                          JToken j3;
                          if (j2.Children().Count() > 0) {
                              j3 = j2.Children().First() as JToken;
                          } else {
                              continue;
                          }
                          Console.WriteLine("****** STARTING SECOND INNER...");
                          for (int c = 0; c < j2.Children().Count(); c++) {
                              if (j3 == null)
                                  continue;
                              if (c > 0) {
                                  j3 = j3.Next;
                                  if (j3 == null)
                                      continue;
                              }
                              var r = j3 as JProperty;
                              if (r == null) {
                                  continue;
                              } // r null check
          
                              Console.WriteLine("FOR 2 = "
                                  + a.ToString() + ","
                                  + b.ToString() + ","
                                  + c.ToString() + " --- " + r.Name);
                              // DO STUFF HERE.
          
                              // THIRD INNER LOOP
                              // Set up a JToken or continue
                              JToken j4;
                              if (j3.Children().Count() > 0) {
                                  j4 = j3.Children().First() as JToken;
                              } else {
                                  continue;
                              }
          
                              Console.WriteLine("********* STARTING THIRD INNER...");
                              for (int d = 0; d < j3.Children().Count(); d++) {
                                  if (j4 == null)
                                      continue;
                                  if (c > 0) {
                                      j4 = j4.Next;
                                      if (j4 == null)
                                          continue;
                                  }
                                  var s = j4 as JProperty;
                                  if (s == null) {
                                      continue;
                                  } // s null check
          
                                  Console.WriteLine("FOR 3 = "
                                      + a.ToString() + ","
                                      + b.ToString() + ","
                                      + c.ToString() + ","
                                      + d.ToString() + " --- " + s.Name);
                                  // DO STUFF HERE.
                                  // ...
          
                              } // for d - j3
                          } // for c - j2
                      } // for b - j1
                  } // for a - original JObject
          

          【讨论】:

          • 你的速度明显快了吗?还是你在猜? o.Children().Count() 假设每次迭代的计数都比实现者选择的要快。这通常不是真的,或者将来可能会改变。迭代器和封装的全部意义在于您不应该知道或关心。例如,如果 Children 是一个链表,则 Next() 非常快,而 Count 可能非常慢。同样 for (int i....) 在迭代时不会检测集合是否已更改(来自另一个线程)。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-07-18
          • 2020-01-02
          • 1970-01-01
          • 2018-09-05
          • 2019-11-16
          • 2021-12-20
          • 2021-06-28
          相关资源
          最近更新 更多