迭代 json 输入并基于“键”创建类似树视图的层次结构,同时考虑到子“键”

2024-03-13

我有以下代码:

 JObject my_obj = JsonConvert.DeserializeObject<JObject>(ReceivedJson);
 ParseJson(my_obj); //method to store all the nested "keys" and the "id" values

     public void ParseJson(JObject obj)
        {

            foreach (KeyValuePair<string, JToken> sub_obj in (JObject)obj["Soccer"])
            {
                Console.WriteLine(sub_obj.Key);
            }
        }
//this does not work well as I cant access all the nested keys :/

我收到以下格式的 json。它可以嵌套在多个级别上,我希望能够将嵌套的“键”及其各自的“T_id”值存储在字典中。

json如下:

{
   "Soccer":{
      "T_id":0,
      "T_state":"valid",
      "Clubs":{
         "ClubA":{
            "T_id":"1",
            "T_state":"Champs"
         },
         "ClubB":{
            "T_id":"2",
            "T_state":"Runnerups"
         }
      },
      "Subs":{
         "SubA":{
            "T_id":"3",
            "T_state":"Unfit",
            //this is nested key
            "SubE":{
               "T_id":"3",
               "T_state":"Unfit"
            }
         }
      },
      "Subs_Used":{
         "SubK":{
            "T_id":"3",
            "T_state":"Unfit"
         }
      }
      //many more nested n-levels   
   }
}

我希望能够提取“键”并创建一个像这样的嵌套结构:

>Soccer
  >Clubs
    ClubA
    ClubB
  >Subs
    SubA
  >Subs_Used
    SubK

其中每个节点有两个字段,{ string key, int T_id }

“键”可以嵌套得很深,我想要一个通用方法,它允许我在迭代 JObject 时创建这个层次结构。

有没有一种简单的方法可以做到这一点?我真的很迷茫,希望能得到帮助以取得进步。


您想要做的是将深度嵌套的 JSON 映射到 C# 树中,其中每个节点都有两个属性 -string key and a long T_id——以及相同类型的子代的集合。

您可以使用列表对其进行如下建模:

public partial class KeyIdObject
{
    public string key { get; set; }
    public long T_id { get; set; }
    public List<KeyIdObject> Children { get; set; }
}

有了数据模型后,您需要使用递归算法来生成节点。相关算法如图在 JObject 层次结构中按名称搜索特定的 JToken https://stackoverflow.com/q/19645501/3744182,但是您需要两阶段递归:

  • 下降通过JToken层次结构,直到找到一个JObject with a T_id财产。

  • 找到匹配项后,构建一个KeyIdObject并通过使用嵌套递归搜索来搜索匹配的 JObject 子级来填充其子级列表。

  • 然后继续进行外部递归搜索中匹配的下一个同级。

这可以通过引入搜索扩展方法来实现topmost给定的后代JToken匹配给定条件:

public static partial class JsonExtensions
{
    /// <summary>
    /// Enumerates through all descendants of the given element, returning the topmost elements that match the given predicate
    /// </summary>
    /// <param name="root"></param>
    /// <param name="filter"></param>
    /// <returns></returns>
    public static IEnumerable<TJToken> TopDescendantsWhere<TJToken>(this JToken root, Func<TJToken, bool> predicate) where TJToken : JToken
    {
        if (predicate == null)
            throw new ArgumentNullException();
        return GetTopDescendantsWhere<TJToken>(root, predicate, false);
    }

    static IEnumerable<TJToken> GetTopDescendantsWhere<TJToken>(JToken root, Func<TJToken, bool> predicate, bool includeSelf) where TJToken : JToken
    {
        if (root == null)
            yield break;
        if (includeSelf)
        {
            var currentOfType = root as TJToken;
            if (currentOfType != null && predicate(currentOfType))
            {
                yield return currentOfType;
                yield break;
            }
        }
        var rootContainer = root as JContainer;
        if (rootContainer == null)
            yield break;
        var current = root.First;
        while (current != null)
        {
            var currentOfType = current as TJToken;
            var isMatch = currentOfType != null && predicate(currentOfType);
            if (isMatch)
                yield return currentOfType;

            // If a match, skip children, but if not, advance to the first child of the current element.
            var next = (isMatch ? null : current.FirstChild());

            if (next == null)
                // If no first child, get the next sibling of the current element.
                next = current.Next;

            // If no more siblings, crawl up the list of parents until hitting the root, getting the next sibling of the lowest parent that has more siblings.
            if (next == null)
            {
                for (var parent = current.Parent; parent != null && parent != root && next == null; parent = parent.Parent)
                {
                    next = parent.Next;
                }
            }

            current = next;
        }
    }

    static JToken FirstChild(this JToken token)
    {
        var container = token as JContainer;
        return container == null ? null : container.First;
    }
}

然后,您可以使用它来生成递归List<KeyIdObject>像这样:

public partial class KeyIdObject
{
    public static List<KeyIdObject> ToIdObjects(JToken root)
    {
        return root.TopDescendantsWhere<JObject>(o => o["T_id"] != null)
            .Select(o => new KeyIdObject { key = ((JProperty)o.Parent).Name, T_id = (long)o["T_id"], Children = ToIdObjects(o) })
            .ToList();
    }
}

演示小提琴#1here https://dotnetfiddle.net/KCwAi6,生成以下结构:

[
  {
    "key": "Soccer",
    "T_id": 0,
    "Children": [
      {
        "key": "ClubA",
        "T_id": 1
      },
      {
        "key": "ClubB",
        "T_id": 2
      },
      {
        "key": "SubA",
        "T_id": 3,
        "Children": [
          {
            "key": "SubE",
            "T_id": 3
          }
        ]
      },
      {
        "key": "SubK",
        "T_id": 3
      }
    ]
  }
]

但是,在 JSON 中的一些对象节点,特别是"Clubs" and "Subs",没有T_id财产。因此,它们无法被捕获到节点层次结构中,因为无法填充long T_id价值。如果您确实需要捕获这些节点,您可以修改数据模型,使 id 具有可为空的值,并捕获中间节点,如下所示:

public partial class KeyIdObject
{
    public string key { get; set; }
    public long? T_id { get; set; }
    public List<KeyIdObject> Children { get; set; }
}

public partial class KeyIdObject
{
    public static List<KeyIdObject> ToIdObjects(JToken root)
    {
        return root.TopDescendantsWhere<JObject>(o => true)
            .Select(o => new KeyIdObject { key = ((JProperty)o.Parent).Name, T_id = (long?)o["T_id"], Children = ToIdObjects(o) })
            .ToList();
    }
}

演示小提琴#2here https://dotnetfiddle.net/XtQUzD.

最后,如果您确定您的键在任何给定级别都是唯一的,您可以使用字典而不是列表,如下所示:

public partial class IdObject
{
    public long T_id { get; set; }
    public Dictionary<string, IdObject> Children { get; set; }
}

public partial class IdObject
{
    public static Dictionary<string, IdObject> ToIdObjects(JToken root)
    {
        return root.TopDescendantsWhere<JObject>(o => o["T_id"] != null)
            .ToDictionary(o => ((JProperty)o.Parent).Name, 
                          o => new IdObject { T_id = (long)o["T_id"], Children = ToIdObjects(o) });
    }
}

演示小提琴 #3here https://dotnetfiddle.net/4x1VkW.

请注意,在所有情况下,我都选择long代替int为了T_id为了安全。


Update

如果您要将其绑定到 WPFTreeView https://learn.microsoft.com/en-us/dotnet/api/system.windows.controls.treeview或类似的东西Syncfusion.Xamarin.SfTreeView https://help.syncfusion.com/xamarin/sftreeview/getting-started,你会想要实现INotifyPropertyChanged并使用ObservableCollection<T>。您可能还想使用不同的ItemTemplate对于有和没有的节点T_id值,在这种情况下,您可以为每种情况定义不同的 c# POCO。下面是一个例子:

public abstract partial class KeyItemBase : INotifyPropertyChanged
{
    public KeyItemBase() : this(null, Enumerable.Empty<KeyItemBase>()) { }

    public KeyItemBase(string key, IEnumerable<KeyItemBase> children)
    {
        this.m_key = key;
        this.m_children = new ObservableCollection<KeyItemBase>(children);
    }

    string m_key;
    public string key 
    { 
        get { return m_key; }
        set
        {
            m_key = value;
            RaisedOnPropertyChanged("key");
        }
    }

    ObservableCollection<KeyItemBase> m_children;
    public ObservableCollection<KeyItemBase> Children { get { return m_children; } }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void RaisedOnPropertyChanged(string _PropertyName)
    {
        var changed = PropertyChanged;
        if (changed != null)
        {
            changed(this, new PropertyChangedEventArgs(_PropertyName));
        }
    }
}

public abstract partial class KeyItemBase
{
    // Generate clean JSON on re-serialization.
    public bool ShouldSerializeChildren() { return Children != null && Children.Count > 0; }
}

public sealed class KeyItem : KeyItemBase
{
    // Use for a JSON object with no T_id property.
    // Bind an appropriate SfTreeView.ItemTemplate to this type.

    public KeyItem() : base() { }

    public KeyItem(string key, IEnumerable<KeyItemBase> children) : base(key, children) { }
}

public class KeyIdItem : KeyItemBase
{
    // Use for a JSON object with a T_id property.
    // Bind an appropriate SfTreeView.ItemTemplate to this type.

    public KeyIdItem() : base() { }

    public KeyIdItem(string key, IEnumerable<KeyItemBase> children, long t_id) : base(key, children) { this.m_id = t_id; }

    long m_id;
    public long T_id 
    { 
        get { return m_id; }
        set
        {
            m_id = value;
            RaisedOnPropertyChanged("T_id");
        }
    }
}

public static class KeyItemFactory
{
    public static KeyItemBase ToKeyObject(string name, long? id, IEnumerable<KeyItemBase> children)
    {
        if (id == null)
            return new KeyItem(name, children);
        else
            return new KeyIdItem(name, children, id.Value);
    }

    public static IEnumerable<KeyItemBase> ToKeyObjects(JToken root)
    {
        return root.TopDescendantsWhere<JObject>(o => true)
            .Select(o => ToKeyObject(((JProperty)o.Parent).Name, (long?)o["T_id"], ToKeyObjects(o)));
    }
}

您将按如下方式使用:

var items = new ObservableCollection<KeyItemBase>(KeyItemFactory.ToKeyObjects(root));

// Now bind items to your ItemsSource
// https://help.syncfusion.com/cr/cref_files/xamarin/Syncfusion.SfTreeView.XForms~Syncfusion.XForms.TreeView.SfTreeView~ItemsSource.html

演示小提琴#4here https://dotnetfiddle.net/Pc3wYf.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

迭代 json 输入并基于“键”创建类似树视图的层次结构,同时考虑到子“键” 的相关文章

  • 调用 McAfee 病毒扫描引擎

    我收到客户的请求 要求使用他们服务器上的 McAfee 病毒扫描将病毒扫描集成到应用程序中 我做了一些调查 发现 McScan32 dll 是主要的扫描引擎 它导出各种看起来有用的函数 我还发现提到了 McAfee Scan Engine
  • 在 xaml 中编写嵌套类型时出现设计时错误

    我创建了一个用户控件 它接受枚举类型并将该枚举的值分配给该用户控件中的 ComboBox 控件 很简单 我在数据模板中使用此用户控件 当出现嵌套类型时 问题就来了 我使用这个符号来指定 EnumType x Type myNamespace
  • 如何在 Cassandra 中存储无符号整数?

    我通过 Datastax 驱动程序在 Cassandra 中存储一些数据 并且需要存储无符号 16 位和 32 位整数 对于无符号 16 位整数 我可以轻松地将它们存储为有符号 32 位整数 并根据需要进行转换 然而 对于无符号 64 位整
  • std::vector 与 std::stack

    有什么区别std vector and std stack 显然 向量可以删除集合中的项目 尽管比列表慢得多 而堆栈被构建为仅后进先出的集合 然而 堆栈对于最终物品操作是否更快 它是链表还是动态重新分配的数组 我找不到关于堆栈的太多信息 但
  • 随着时间的推移,添加到 List 变得非常慢

    我正在解析一个大约有 1000 行的 html 表 我从一个字符串中添加 10 个字符串 td 每行到一个list td
  • 传递给函数时多维数组的指针类型是什么? [复制]

    这个问题在这里已经有答案了 我在大学课堂上学习了 C 语言和指针 除了多维数组和指针之间的相似性之外 我认为我已经很好地掌握了这个概念 我认为由于所有数组 甚至多维 都存储在连续内存中 因此您可以安全地将其转换为int 假设给定的数组是in
  • 如何从本机 C(++) DLL 调用 .NET (C#) 代码?

    我有一个 C app exe 和一个 C my dll my dll NET 项目链接到本机 C DLL mynat dll 外部 C DLL 接口 并且从 C 调用 C DLL 可以正常工作 通过使用 DllImport mynat dl
  • -webkit-box-shadow 与 QtWebKit 模糊?

    当时有什么方法可以实现 webkit box shadow 的工作模糊吗 看完这篇评论错误报告 https bugs webkit org show bug cgi id 23291 我认识到这仍然是一个问题 尽管错误报告被标记为RESOL
  • 如何使从 C# 调用的 C(P/invoke)代码“线程安全”

    我有一些简单的 C 代码 它使用单个全局变量 显然这不是线程安全的 所以当我使用 P invoke 从 C 中的多个线程调用它时 事情就搞砸了 如何为每个线程单独导入此函数 或使其线程安全 我尝试声明变量 declspec thread 但
  • 需要帮助优化算法 - 两百万以下所有素数的总和

    我正在尝试做一个欧拉计划 http projecteuler net问题 我正在寻找 2 000 000 以下所有素数的总和 这就是我所拥有的 int main int argc char argv unsigned long int su
  • 方程“a + bx = c + dy”的积分解

    在等式中a bx c dy 所有变量都是整数 a b c and d是已知的 我如何找到整体解决方案x and y 如果我的想法是正确的 将会有无限多个解 由最小公倍数分隔b and d 但我只需要一个解决方案 我可以计算其余的 这是一个例
  • 两个静态变量同名(两个不同的文件),并在任何其他文件中 extern 其中一个

    在一个文件中将变量声明为 static 并在另一个文件中进行 extern 声明 我认为这会在链接时出现错误 因为 extern 变量不会在任何对象中看到 因为在其他文件中声明的变量带有限定符 static 但不知何故 链接器 瑞萨 没有显
  • WcfSvcHost 的跨域异常

    对于另一个跨域问题 我深表歉意 我一整天都在与这个问题作斗争 现在已经到了沸腾的地步 我有一个 Silverlight 应用程序项目 SLApp1 一个用于托管 Silverlight SLApp1 Web 的 Web 项目和 WCF 项目
  • 两个类可以使用 C++ 互相查看吗?

    所以我有一个 A 类 我想在其中调用一些 B 类函数 所以我包括 b h 但是 在 B 类中 我想调用 A 类函数 如果我包含 a h 它最终会陷入无限循环 对吗 我能做什么呢 仅将成员函数声明放在头文件 h 中 并将成员函数定义放在实现文
  • C 编程:带有数组的函数

    我正在尝试编写一个函数 该函数查找行为 4 列为 4 的二维数组中的最大值 其中二维数组填充有用户输入 我知道我的主要错误是函数中的数组 但我不确定它是什么 如果有人能够找到我出错的地方而不是编写新代码 我将不胜感激 除非我刚去南方 我的尝
  • 为什么 isnormal() 说一个值是正常的,而实际上不是?

    include
  • C 函数 time() 如何处理秒的小数部分?

    The time 函数将返回自 1970 年以来的秒数 我想知道它如何对返回的秒数进行舍入 例如 对于100 4s 它会返回100还是101 有明确的定义吗 ISO C标准没有说太多 它只说time 回报 该实现对当前日历时间的最佳近似 结
  • 编译时展开 for 循环内的模板参数?

    维基百科 here http en wikipedia org wiki Template metaprogramming Compile time code optimization 给出了 for 循环的编译时展开 我想知道我们是否可以
  • 对于某些 PDF 文件,LoadIFilter() 返回 -2147467259

    我正在尝试使用 Adob e IFilter 搜索 PDF 文件 我的代码是用 C 编写的 我使用 p invoke 来获取 IFilter 的实例 DllImport query dll SetLastError true CharSet
  • 在OpenGL中,我可以在坐标(5, 5)处精确地绘制一个像素吗?

    我所说的 5 5 正是指第五行第五列 我发现使用屏幕坐标来绘制东西非常困难 OpenGL 中的所有坐标都是相对的 通常范围从 1 0 到 1 0 为什么阻止程序员使用屏幕坐标 窗口坐标如此严重 最简单的方法可能是通过以下方式设置投影以匹配渲

随机推荐