programing

C# 평탄화 json 구조

shortcode 2023. 4. 3. 23:30
반응형

C# 평탄화 json 구조

C#(Newtonsoft로 표시됨)에 json-object가 있습니다.Json.Linq.JObject 객체)를 사용하여 사전에 평평하게 만들어야 합니다.예를 들어 설명하겠습니다.

{
    "name": "test",
    "father": {
         "name": "test2"
         "age": 13,
         "dog": {
             "color": "brown"
         }
    }
}

그러면 다음과 같은 키-값 쌍이 있는 사전이 생성됩니다.

["name"] == "test",
["father.name"] == "test2",
["father.age"] == 13,
["father.dog.color"] == "brown"

이거 어떻게 해?

JObject jsonObject=JObject.Parse(theJsonString);
IEnumerable<JToken> jTokens = jsonObject.Descendants().Where(p => !p.HasValues);
Dictionary<string, string> results = jTokens.Aggregate(new Dictionary<string, string>(), (properties, jToken) =>
                    {
                        properties.Add(jToken.Path, jToken.ToString());
                        return properties;
                    });

네스트된 json 구조를 사전 객체에 평평하게 하는 것과 같은 요건이 있었습니다.여기서 해결책을 찾았습니다.

.NET에서는 Core 3.0이 하나의 방법입니다(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
}

보다 표현력이 풍부한 버전을 다음에 나타냅니다.

시험

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));

산출량

{
  "name": "test",
  "father.name": "test2",
  "father.age": 13,
  "father.dog.color": "brown"
}

보다 표현력 높은 버전

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;
    }

사실 오늘 아침 SO에서 이 질문을 찾을 수 없었고 결국 저만의 확장 방법을 써서 반품하게 되었습니다.JValueJSON 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
}

그럼 내 통화 코드에서, 난 그냥 그 코드를Path그리고.Value로부터의JValue반환된 객체:

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

https://github.com/jsonfx/jsonfx을 사용하여 json을 동적 개체로 역직렬화할 수 있습니다.그런 다음 Expando Object를 사용하여 원하는 것을 얻습니다.

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);
                    }
                }
            }
        }

JSONPath를 사용할 수 있습니다.$..*JSON 구조의 모든 멤버를 가져오고 하위 항목이 없는 멤버를 필터링하여 컨테이너 속성을 건너뜁니다.

예.

var schemaObject = JObject.Parse(schema);
var values = schemaObject
    .SelectTokens("$..*")
    .Where(t => !t.HasValues)
    .ToDictionary(t => t.Path, t => t.ToString());

tymtam이 제공하는 코드와 지원되는 어레이를 기반으로 합니다.

    public static IEnumerable<KeyValuePair<string, string>> Flatten<T>(this T data, string seperator = ":") where T : class
    {
        var json = JsonSerializer.Serialize(data);

        string GetArrayPath(string path, string name, int idx) =>
            path == null ? $"{name}{seperator}{idx}" : $"{path}{seperator}{name}{seperator}{idx}";
        IEnumerable<(string Path, JsonElement Element)> GetLeaves(string path, string name, JsonElement element) => element.ValueKind switch
        {
            JsonValueKind.Object => element.EnumerateObject()
                .SelectMany(child => GetLeaves(path == null ? name : $"{path}{seperator}{name}", child.Name, child.Value)),
            JsonValueKind.Array => element.EnumerateArray()
                .SelectMany((child, idx) => child.ValueKind == JsonValueKind.Object
                        ? child.EnumerateObject().SelectMany(child => GetLeaves(GetArrayPath(path, name, idx), child.Name, child.Value))
                        : new[] { (Path: GetArrayPath(path, name, idx), child) }
                    ),
            _ => new[] { (Path: path == null ? name : $"{path}{seperator}{name}", element) },
        };

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

언급URL : https://stackoverflow.com/questions/7394551/c-sharp-flattening-json-structure

반응형