【问题标题】:Flattening out a JToken展平 JToken
【发布时间】:2014-12-30 23:08:34
【问题描述】:

假设我有以下 JToken:

@"{
    ""data"": [
        {
            ""company"": {
                ""ID"": ""12345"",
                ""location"": ""Some Location""
            },
            ""name"": ""Some Name""
        }
    ]
}";

我想将此令牌传递给输出此 JToken 的 FlattenToken 函数:

@"{
    ""data"": [
        {
            ""company_ID"": ""12345"",
            ""company_location"": ""Some Location"",
            ""name"": ""Some Name""
        }
]}"

这样做的原因是我可以将扁平化的 JToken 反序列化为 DataTable。

不过,我迷失在一堆 JObject、JToken、JProperties 和其他 JMadness 中。我在this post 上看到了答案,这很有帮助,但我仍然没有做对。

这是我目前所拥有的:

public static JToken FlattenToken(JToken token)
{
    foreach (JToken topLevelItem in token["data"].Children())
    {
        foreach (JToken field in topLevelItem.Value<JToken>())
        {
            foreach (JProperty property in field.Value<JObject>().Properties())
            {
                field.AddAfterSelf(JObject.Parse(@"{""" + property.Name + "_" + property.Value));
            }

            field.Remove();
        }
    }

    return token;
}

通过外部foreach循环的第一次迭代,topLevelItem =

{
    "company": {
        "ID": "12345"
    },
    "name": "Some Name"
}

而第一次迭代通过第二个foreach循环,field =

"company": {
    "ID": "12345"
}

到目前为止看起来不错。但是当我点击最里面的 foreach 循环时,我在 foreach 行上出现异常:“无法将 Newtonsoft.Json.Linq.JProperty 转换为 Newtonsoft.Json.Linq.JToken。”

不确定那里发生了什么。我的印象是 field.Value 调用将生成一个 JToken 并尝试将其转换为 JProperty。那么,正如错误所暗示的,JProperty 在哪里试图被强制转换为 JToken?

此外,这感觉像是一种将 JToken 扁平化的非常粗暴的方式。有没有更好的办法?

【问题讨论】:

    标签: c# json json.net


    【解决方案1】:

    Json.NET 中的对象层次结构可能相当深。粗略的指南可以在this answer 中找到。

    要解决您的问题,您首先需要一个扩展方法来获取 JObject 的属性,然后在带有名称前缀的集合中返回:

    public static class JsonExtensions
    {
        public static IEnumerable<KeyValuePair<string, JToken>> FlattenFields(this JObject obj, string prefix)
        {
            foreach (var field in obj)
            {
                string fieldName = prefix + "_" + field.Key;
                var fieldValue = field.Value;
                yield return new KeyValuePair<string, JToken>(fieldName, fieldValue);
            }
        }
    }
    

    接下来,您需要一些递归工具来遍历 Json.NET 层次结构并重写所选JObject 的属性集合:

    public static class JsonExtensions
    {
        public static IEnumerable<T> Yield<T>(this T item)
        {
            yield return item;
        }
    
        public static JToken EditFields(this JToken token, Func<KeyValuePair<string, JToken>, IEnumerable<KeyValuePair<string, JToken>>> editor)
        {
            if (token == null)
                return null;
            switch (token.Type)
            {
                case JTokenType.Array:
                    return EditFields((JArray)token, editor);
                case JTokenType.Object:
                    return EditFields((JObject)token, editor);
                default:
                    return token;
            }
        }
    
        static JToken EditFields(JArray array, Func<KeyValuePair<string, JToken>, IEnumerable<KeyValuePair<string, JToken>>> editor)
        {
            JArray newArray = null;
            foreach (var element in array)
            {
                var newElement = EditFields(element, editor);
                if (newElement != null)
                {
                    if (newArray == null)
                        newArray = new JArray();
                    newArray.Add(newElement);
                }
            }
            return newArray;
        }
    
        static JToken EditFields(JObject obj, Func<KeyValuePair<string, JToken>, IEnumerable<KeyValuePair<string, JToken>>> editor)
        {
            JObject newObj = null;
    
            foreach (var field in obj)
            {
                foreach (var newField in editor(field))
                {
                    if (newObj == null)
                        newObj = new JObject();
                    newObj[newField.Key] = newField.Value.EditFields(editor);
                }
            }
    
            return newObj;
        }
    }
    

    最后,将这些放在一起创建一个方法,将命名JObject 属性的属性提升为其父JObject,在属性名称前加上下划线:

    public static class JsonExtensions
    {
        public static JToken PromoteNamedPropertiesToParents(this JToken token, string propertyName)
        {
            return token.EditFields(pair =>
            {
                if (pair.Key == propertyName && pair.Value is JObject)
                {
                    return ((JObject)pair.Value).FlattenFields(pair.Key);
                }
                return pair.Yield();
            });
        }
    }
    

    然后,进行测试:

    public static class TestFlatten
    {
        public static void Test()
        {
            string jsonString = @"{
                ""data"": [
                    {
                        ""company"": {
                            ""ID"": ""12345"",
                            ""location"": ""Some Location""
                        },
                        ""name"": ""Some Name""
                    }
                ]
            }";
            JObject obj = JObject.Parse(jsonString);
            var newObj = (JObject)obj.PromoteNamedPropertiesToParents("company");
            Debug.WriteLine(newObj);
        }
    }
    

    输出是:

    {
      "data": [
        {
          "company_ID": "12345",
          "company_location": "Some Location",
          "name": "Some Name"
        }
      ]
    }
    

    这就是你想要的。请注意,此代码会创建一个新的JObject 层次结构,而不是修改原始层次结构。

    【讨论】:

    • 这太棒了。很有帮助!
    猜你喜欢
    • 2021-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多