c# - 展平 JToken

标签 c# json json.net

假设我有以下 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、JTokens、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 的方式。有没有更好的办法?

最佳答案

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 层次结构,而不是修改原始层次结构。

关于c# - 展平 JToken,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27713897/

相关文章:

java - 是否可以将变量用作@JsonRootName?

c# - 将空 String 反序列化为 List<string>

.net - DI 和 JSON.NET

c# - 更改 ViewModel 外部的 TextBox.Text

c# - 计算 2 个日期时间之间的差异(Timespan + double)

c# - 如何提高 C# 对象映射代码的性能

c# - 将 VB.NET Winforms 应用程序移植到 C#

json - 雅虎Weather-API响应为JSON

json - Hive:json SerDe 文件在外部表中返回 "NULL"

c# - 如何使用嵌套的对象数组反序列化 JSON 数组