【问题标题】:Custom sorting Dictionary<string, JToken> traverse down using LINQ自定义排序 Dictionary<string, JToken> 使用 LINQ 向下遍历
【发布时间】:2019-04-12 14:06:40
【问题描述】:

我目前将Dictionary&lt;string, JToken&gt; item 定义为对象类。这不是根对象,而是第二级。

public class Root
{
    public string name { get; set; }
    public string description { get; set; }
    public Table Table {get; set; }

}
public class Table
{
    public Name name { get; set; }
    [JsonExtensionData]
    public Dictionary<string, JToken> item { get; set; }

}

有了这个,我希望在这个类中反序列化和排序传入的 json。目前所有的 json 对象都是无序的。我希望使用每个对象中的属性来创建订单。

请查看 JSON 结构:

 {
        'name': 'Customer Table',
        'Table': {
            'name': {
                'id': 'name',
                'type': 'info'
                'description': 'Customer table info'
            },
            'RowId-123': {
                'id': 'RowId-123',
                'type': 'Row',
                'children': [
                    'ColumnId-367'
                ],
                'index': 1,
                'parentId': 'custom'
            },
            'ColumnId-367': {
                'id': 'ColumnId-367',
                'type': 'Column',
                'children': [],
                'parentId': 'RowId-123'
            },
            'RowId-476': {
                'id': 'RowId-476',
                'type': 'Row'
                'components': [
                    'ColumnId-317',
                    'ColumnId-327'
                ],
                'index': 2,
                'parentId': 'custom'
            },
            'ColumnId-317': {
                'id': 'ColumnId-317',
                'type': 'Column'
                'components': [],
                'index': 0,
                'parentId': 'RowId-476'
            },
            'ColumnId-327': {
                'id': 'ColumnId-327',
                'type': 'Column',
                'components': [],
                'index': 1,
                'parentId': 'RowId-476'
            },
            'TextContent12': {
                'id': 'TextContent12',
                'type': 'Text',
                'index': 0,
                'parentId': 'custom'
            }
        }
    }

我在下面有 Linq 查询。

           //Get all objects that has parent custom top level layer.
            var top = root.Table.item.Values
            .Where(x => x["parentId"].Value<string>() == "custom")
            .OrderBy(i => i["index"]);

            //Only looks at Index 0 and Row type.
            var row = root.Table.item.Values
            .FirstOrDefault(x => x["type"].Value<string>() == "Row" && x["index"].Value<int>() == 0);

            //Get children column of row.
            var column = root.Table.item.Values
            .Where(x => x["parentId"]?.Value<string>() == row["id"].Value<string>())
            .OrderBy(i => i["index"]);

我正在努力组合我的 linq 查询以循环遍历索引并获得我想要的输出。如何将查询组合在一起,这将根据不在行或行对象内的 Item 的最大值增加索引以产生以下输出。

Index 0 Content:
'TextContent12': {
                'id': 'TextContent12',
                'type': 'Text',
                'index': 0,
                'parentId': 'custom'
            }


Index 1 Content:
 'RowId-123': {
                'id': 'RowId-123',
                'type': 'Row',
                'children': [
                    'ColumnId-367'
                ],
                'index': 1,
                'parentId': 'custom'
            },
            'ColumnId-367': {
                'id': 'ColumnId-367',
                'type': 'Column',
                'children': [],
                'parentId': 'RowId-123'
            }

Index 2 Content:

'RowId-476': {
                'id': 'RowId-476',
                'type': 'Row'
                'components': [
                    'ColumnId-317',
                    'ColumnId-327'
                ],
                'index': 2,
                'parentId': 'custom'
            },
            'ColumnId-317': {
                'id': 'ColumnId-317',
                'type': 'Column'
                'components': [],
                'index': 0,
                'parentId': 'RowId-476'
            },
            'ColumnId-327': {
                'id': 'ColumnId-327',
                'type': 'Column',
                'components': [],
                'index': 1,
                'parentId': 'RowId-476'
            }

【问题讨论】:

    标签: c# json linq json.net


    【解决方案1】:

    您实际上并不需要知道最大索引来循环遍历项目。您已经使用OrderBy 来确保它们按索引排列。所以你只需要在每个级别都有一个foreach 循环。如果想知道当前项在循环体内的索引是多少,可以使用item["index"]获取,如下图:

    认为这就是你要找的东西:

    // Get all objects at the top level (parentId is "custom") ordered by index
    var top = root.Table.item.Values
        .Where(x => x["parentId"].Value<string>() == "custom")
        .OrderBy(i => i["index"]);
    
    // Loop through top level items 
    foreach (JToken item in top)
    {
        Console.WriteLine("Index " + item["index"] + " Content:");
        Console.WriteLine(item.ToString());  // output item JSON - could be Row or something else 
    
        // Check whether the current item is a Row
        if (item["type"].Value<string>() == "Row")
        {
            // If so, get all items which have this row as a parent ordered by index
            var children = root.Table.item.Values
                .Where(x => x["parentId"].Value<string>() == item["id"].Value<string>())
                .OrderBy(i => i["index"]);
    
            // loop through the child items
            foreach (JToken child in children)
            {
                Console.WriteLine(child.ToString());  // output child JSON
            }
        }
    
        Console.WriteLine();
    }
    

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

    顺便说一句,您的 JSON 存在一些不一致之处,我不确定这些是否是预期的,或者您是否手动编辑 JSON 并因此引入错误(似乎很可能)。例如:

    • ColumnId-367 没有 index 值,而所有其他项目都有。由于我们试图通过index 进行排序,这引发了一些关于索引丢失时应该发生什么的问题。如果这是真的可能,您可能需要对代码进行一些调整以解决此问题。
    • RowId-123 有一个 children 数组,而 RowId-476 有一个 components 数组。出于这个问题的目的,这似乎并不重要,因为我们使用ParentId 来关联这些项目。但这仍然让我想知道哪个是正确的,以及这是否会导致以后出现问题。
    • 整个 JSON 中的各种属性之间缺少逗号分隔符,这将导致无法正确解析。

    记住,“garbage in, garbage out”。

    【讨论】:

    • 感谢这个布赖恩。我把它弄得太复杂了,并且已经有了一些 LINQ 查询中需要的信息。我可以问如何将每个索引作为单个对象传递给方法吗?因此,例如,此方法将在索引 0 中看到它的类型文本并提取一些属性。我是否将它们添加到 列表中?我对 JToken、JObject 等还很陌生。
    • 没有什么能阻止你将JToken 传递给另一个方法。让您的方法签名接受 JToken 参数,例如void MyMethod(JToken item)。然后在方法中,您可以提取属性值,如我在上面的答案中所示,例如var type = item["type"].Value&lt;string&gt;()。另一种选择是创建一个模型类,然后使用item.ToObject&lt;YourClass&gt;()JToken 填充它。然后,您可以将该类传递给您的方法。如果你有多个项目要传递,那么当然,将它们作为列表传递是有意义的(JToken 或模型类)。
    • 您能否提供一个将每个索引范围传递给 mymethod(JToken 项)的示例。因为我有点困惑,感觉我会为我想要实现的目标编写重复的代码。我已将每个对象打印到此方法,但是我想将每个索引传递给它们的关联方法,例如索引 0 只是一个文本,我想传递这个方法,它将它包装在一个 Row/Column 对象中。完成后移至索引 1 检查存在的对象。我需要编写相同的 linq 来选择对象还是线性读取?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-17
    • 2020-02-02
    相关资源
    最近更新 更多