C#flattening json结构

Alx*_*ndr 22 c# recursion json data-structures

我在C#中有一个json对象(表示为Newtonsoft.Json.Linq.JObject对象),我需要将其展平为字典.让我举例说明我的意思:

{
    "name": "test",
    "father": {
         "name": "test2"
         "age": 13,
         "dog": {
             "color": "brown"
         }
    }
}
Run Code Online (Sandbox Code Playgroud)

这应该产生一个包含以下键值对的字典:

["name"] == "test",
["father.name"] == "test2",
["father.age"] == 13,
["father.dog.color"] == "brown"
Run Code Online (Sandbox Code Playgroud)

我怎样才能做到这一点?

Sar*_*uri 27

JObject jsonObject=JObject.Parse(theJsonString);
IEnumerable<JToken> jTokens = jsonObject.Descendants().Where(p => p.Count() == 0);
Dictionary<string, string> results = jTokens.Aggregate(new Dictionary<string, string>(), (properties, jToken) =>
                    {
                        properties.Add(jToken.Path, jToken.ToString());
                        return properties;
                    });
Run Code Online (Sandbox Code Playgroud)

我有一个将嵌套的json结构展平为字典对象的相同要求.在这里找到解决方案.

  • 我爱你.你救了我的命 (3认同)

wat*_*ewp 7

事实上,今天早些时候我也遇到了同样的问题,一开始无法在 SO 上找到这个问题,最终编写了我自己的扩展方法来返回JValue包含 JSON blob 叶节点值的对象。它与已接受的答案类似,但有一些改进:

  1. 它处理您提供给它的任何 JSON(数组、属性等),而不仅仅是 JSON 对象。
  2. 更少的内存使用
  3. 不会调用.Count()您最终不需要的后代

根据您的用例,这些可能相关也可能不相关,但它们适合我的情况。我在博客上写了有关学习扁平化 JSON.NET 对象的文章。这是我写的扩展方法:

public static class JExtensions
{
    public static IEnumerable<JValue> GetLeafValues(this JToken jToken)
    {
        if (jToken is JValue jValue)
        {
            yield return jValue;
        }
        else if (jToken is JArray jArray)
        {
            foreach (var result in GetLeafValuesFromJArray(jArray))
            {
                yield return result;
            }
        }
        else if (jToken is JProperty jProperty)
        {
            foreach (var result in GetLeafValuesFromJProperty(jProperty))
            {
                yield return result;
            }
        }
        else if (jToken is JObject jObject)
        {
            foreach (var result in GetLeafValuesFromJObject(jObject))
            {
                yield return result;
            }
        }
    }

    #region Private helpers

    static IEnumerable<JValue> GetLeafValuesFromJArray(JArray jArray)
    {
        for (var i = 0; i < jArray.Count; i++)
        {
            foreach (var result in GetLeafValues(jArray[i]))
            {
                yield return result;
            }
        }
    }

    static IEnumerable<JValue> GetLeafValuesFromJProperty(JProperty jProperty)
    {
        foreach (var result in GetLeafValues(jProperty.Value))
        {
            yield return result;
        }
    }

    static IEnumerable<JValue> GetLeafValuesFromJObject(JObject jObject)
    {
        foreach (var jToken in jObject.Children())
        {
            foreach (var result in GetLeafValues(jToken))
            {
                yield return result;
            }
        }
    }

    #endregion
}
Run Code Online (Sandbox Code Playgroud)

然后在我的调用代码中,我只是从返回的对象中提取Path和属性:ValueJValue

var jToken = JToken.Parse("blah blah json here"); 
foreach (var jValue in jToken.GetLeafValues()) 
{
    Console.WriteLine("{0} = {1}", jValue.Path, jValue.Value);
}
Run Code Online (Sandbox Code Playgroud)


tym*_*tam 7

从 .NET Core 3.0 开始JsonDocument是一种方式(不需要 Json.NET)。我相信这会变得更容易。

using System.Linq;
using System.Text.Json;
(...)


public static Dictionary<string, JsonElement> GetFlat(string json)
{
    IEnumerable<(string Path, JsonProperty P)> GetLeaves(string path, JsonProperty p)
        => p.Value.ValueKind != JsonValueKind.Object
            ? new[] { (Path: path == null ? p.Name : path + "." + p.Name, p) }
            : p.Value.EnumerateObject() .SelectMany(child => GetLeaves(path == null ? p.Name : path + "." + p.Name, child));

    using (JsonDocument document = JsonDocument.Parse(json)) // Optional JsonDocumentOptions options
        return document.RootElement.EnumerateObject()
            .SelectMany(p => GetLeaves(null, p))
            .ToDictionary(k => k.Path, v => v.P.Value.Clone()); //Clone so that we can use the values outside of using
}
Run Code Online (Sandbox Code Playgroud)

下面显示了一个更具表现力的版本。

测试

using System.Linq;
using System.Text.Json;
(...)

var json = @"{
    ""name"": ""test"",
    ""father"": {
            ""name"": ""test2"", 
         ""age"": 13,
         ""dog"": {
                ""color"": ""brown""
         }
        }
    }";

var d = GetFlat(json);
var options2 = new JsonSerializerOptions { WriteIndented = true };
Console.WriteLine(JsonSerializer.Serialize(d, options2));
Run Code Online (Sandbox Code Playgroud)

输出

{
  "name": "test",
  "father.name": "test2",
  "father.age": 13,
  "father.dog.color": "brown"
}
Run Code Online (Sandbox Code Playgroud)

更具表现力的版本

using System.Linq;
using System.Text.Json;
(...)

static Dictionary<string, JsonElement> GetFlat(string json)
    {
        using (JsonDocument document = JsonDocument.Parse(json))
        {
            return document.RootElement.EnumerateObject()
                .SelectMany(p => GetLeaves(null, p))
                .ToDictionary(k => k.Path, v => v.P.Value.Clone()); //Clone so that we can use the values outside of using
        }
    }


    static IEnumerable<(string Path, JsonProperty P)> GetLeaves(string path, JsonProperty p)
    {
        path = (path == null) ? p.Name : path + "." + p.Name;
        if (p.Value.ValueKind != JsonValueKind.Object)
            yield return (Path: path, P: p);
        else
            foreach (JsonProperty child in p.Value.EnumerateObject())
                foreach (var leaf in GetLeaves(path, child))
                    yield return leaf;
    }
Run Code Online (Sandbox Code Playgroud)

Run Code Online (Sandbox Code Playgroud)


ben*_*obb 5

您可以使用https://github.com/jsonfx/jsonfx将 json 反序列化为动态对象。然后使用 ExpandoObject 来获得你想要的。

public Class1()
        {
            string json = @"{
                                ""name"": ""test"",
                                ""father"": {
                                     ""name"": ""test2"",
                                     ""age"": 13,
                                     ""dog"": {
                                         ""color"": ""brown""
                                     }
                                }
                            }";

            var reader = new JsonFx.Json.JsonReader();
            dynamic output = reader.Read(json);
            Dictionary<string, object> dict = new Dictionary<string, object>();

            GenerateDictionary((System.Dynamic.ExpandoObject) output, dict, "");
        }

        private void GenerateDictionary(System.Dynamic.ExpandoObject output, Dictionary<string, object> dict, string parent)
        {
            foreach (var v in output)
            {
                string key = parent + v.Key;
                object o = v.Value;

                if (o.GetType() == typeof(System.Dynamic.ExpandoObject))
                {
                    GenerateDictionary((System.Dynamic.ExpandoObject)o, dict, key + ".");
                }
                else
                {
                    if (!dict.ContainsKey(key))
                    {
                        dict.Add(key, o);
                    }
                }
            }
        }
Run Code Online (Sandbox Code Playgroud)