【问题标题】:How to do recursive descent of json using json.net?如何使用 json.net 对 json 进行递归下降?
【发布时间】:2013-04-17 08:57:08
【问题描述】:

我正在尝试使用 json.net 解析 json 文件。文件是这样的

{X:
   {
      Title:"foo",
      xxxx:xxxx
   }
}
{Y:
   {ZZ:
        {Title: "bar",...}
    }
}

我正在尝试递归处理所有具有 Title 属性的对象的结构。但我对JTokenJPropertyJContainerJValueJObject 感到困惑。阅读源代码并没有让我变得更聪明,而且这些示例都没有帮助。我想要一些类似的东西

WalkNode(node, Action<Node> action)
{
    foreach(var child in node.Children)
    {
        Action(child);
        WalkNode(child);
    }
}

Parse()
{
   WalkNode(root, n=>
    {
        if(n["Title"] != null)
        {
           ...
        }
    });
}

【问题讨论】:

  • 一些问题:上面的例子不是有效的JSON。属性 X 和 Y 的包含对象是否应该在一个数组中,或者您是否打算让 X 和 Y 在同一个包含对象中?此外,您需要遍历 JSON 层次结构中的任何位置,还是只是嵌套的对象和属性?

标签: c# recursion json.net recursive-descent


【解决方案1】:

下面的代码应该与您要查找的代码非常接近。我假设有一个外部数组,并且数组可以出现在层次结构中的任何位置。 (如果不是这样,您可以稍微简化一下 WalkNode 方法代码,但它应该可以工作。)

using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace JsonRecursiveDescent
{
    class Program
    {
        static void Main(string[] args)
        {
            string json =
            @"[
                {
                    ""X"":
                    {
                        ""Title"":""foo"",
                        ""xxxx"":""xxxx""
                    }
                },
                {
                    ""Y"":
                    {
                        ""ZZ"":
                        {
                            ""Title"":""bar"",
                            ""xxxx"":""xxxx""
                        }
                    }
                }
            ]";

            JToken node = JToken.Parse(json);

            WalkNode(node, n =>
            {
                JToken token = n["Title"];
                if (token != null && token.Type == JTokenType.String)
                {
                    string title = token.Value<string>();
                    Console.WriteLine(title);
                }
            });
        }

        static void WalkNode(JToken node, Action<JObject> action)
        {
            if (node.Type == JTokenType.Object)
            {
                action((JObject)node);

                foreach (JProperty child in node.Children<JProperty>())
                {
                    WalkNode(child.Value, action);
                }
            }
            else if (node.Type == JTokenType.Array)
            {
                foreach (JToken child in node.Children())
                {
                    WalkNode(child, action);
                }
            }
        }

    }
}

【讨论】:

  • 但是如果我不知道“标题”存在并且我想要所有属性?
  • 这是一个不同的问题。如果您想发布一个新问题来描述您正在尝试做什么,我将很乐意回答。请务必标记它json.net
【解决方案2】:

我想我应该对@BrianRogers WalkNode 方法进行一些小调整,让它更加通用:

private static void WalkNode(JToken node,
                                Action<JObject> objectAction = null,
                                Action<JProperty> propertyAction = null)
{
    if (node.Type == JTokenType.Object)
    {
        if (objectAction != null) objectAction((JObject) node);

        foreach (JProperty child in node.Children<JProperty>())
        {
            if (propertyAction != null) propertyAction(child);
            WalkNode(child.Value, objectAction, propertyAction);
        }
    }
    else if (node.Type == JTokenType.Array)
    {
        foreach (JToken child in node.Children())
        {
            WalkNode(child, objectAction, propertyAction);
        }
    }
}

然后 OP 可以执行以下操作:

WalkNode(json, null, prop =>
{
     if (prop.Name == "Title" && prop.Value.Type == JTokenType.String)
     {
         string title = prop.Value<string>();
         Console.WriteLine(title);
     }
});

【讨论】:

    【解决方案3】:

    试试这个方法 试了几次都不成功我写的:

     private void Traverse(JToken token, TreeNode tn)
        {
            if (token is JProperty)
                if (token.First is JValue)
                    tn.Nodes.Add(((JProperty)token).Name + ": " + ((JProperty)token).Value);
                else
                    tn = tn.Nodes.Add(((JProperty)token).Name);
    
            foreach (JToken token2 in token.Children())
                Traverse(token2, tn);
        }
    

    您首先必须将完整的 JSON 文件传递​​给它,如下所示:

    TreeNode rooty= tvu.Nodes.Add("Rooty") // not the Indian bread,just Rooty,  Ok?
    JToken token = JToken.Parse(File.ReadAllText(<"Path to json file">));
    Traverse(token, rooty);
    

    完成了,Boom 你得到了这个: 哦不,我不允许嵌入图片。伤心。

    【讨论】:

      【解决方案4】:

      还需要做一些事情。 想提出我的解决方案。它的优点是:

      • 不是递归的
      • 没有回调
      • 不假设任何内部结构(数组)
      • 将树遍历与需要执行的操作分离

        IEnumerable<JToken> AllTokens(JObject obj) {
            var toSearch = new Stack<JToken>(obj.Children());
            while (toSearch.Count > 0) {
                var inspected = toSearch.Pop();
                yield return inspected;
                foreach (var child in inspected) {
                    toSearch.Push(child);
                }
            }
        }
        

        然后你可以使用 linq 来过滤和执行操作:

        var tokens = AllTokens(jsonObj);
        var titles = tokens.Where(t => t.Type == JTokenType.Property && ((JProperty)t).Name == "Title");
        

      【讨论】:

        【解决方案5】:

        您也可以使用 JSONPath:node.SelectTokens("$..*");

        这样使用:

        var jObjectsWithTitle = node
            .SelectTokens("$..*")
            .OfType<JObject>()
            .Where(x => x.Property("Title") != null);
        

        或者只是:

        var jObjectsWithTitle = node.SelectTokens("$..[?(@.Title)]");
        

        【讨论】:

          【解决方案6】:

          我用“根”节点包装了数据,并将其全部包装为一个数组值。然后这对我有用:

              private static void TraverseJson(string jsonText)
              {
                  dynamic jsonObject = JsonConvert.DeserializeObject(jsonText);
          
                  JArray ja = (JArray)jsonObject.root;
                  int itemCount = ja.Count;
          
                  foreach (JObject jobj in jsonObject.root)
                  {
                      WalkHierarchy(jobj);
                  }
              }
          
              private static void WalkHierarchy(JObject jobj)
              {
                  foreach (JProperty jprop in (JToken)jobj)
                  {
                      if (jprop.Value.Type == JTokenType.Object)
                      {
                          WalkHierarchy((JObject)jprop.Value);
                      }
                      else
                      {
                          Console.WriteLine(jprop.Value.ToString());
                      }
                  }
              }
          

          【讨论】:

            猜你喜欢
            • 2015-02-02
            • 1970-01-01
            • 1970-01-01
            • 2016-04-03
            • 1970-01-01
            • 2015-05-21
            • 1970-01-01
            • 2018-12-25
            相关资源
            最近更新 更多