programing

Json.NET: 중첩된 사전을 역직렬화하는 중

yellowcard 2023. 4. 5. 21:28
반응형

Json.NET: 중첩된 사전을 역직렬화하는 중

오브젝트를 에 역직렬화할 때Dictionary(JsonConvert.DeserializeObject<IDictionary<string,object>>(json)) 네스트된 오브젝트는 에 역직렬화 됩니다.JObjects. 중첩된 오브젝트를 강제로 역직렬화 할 수 있습니까?Dictionarys?

네스트된 모든 오브젝트를Dictionary<string,object>을 제공함으로써CustomCreationConverter구현:

class MyConverter : CustomCreationConverter<IDictionary<string, object>>
{
    public override IDictionary<string, object> Create(Type objectType)
    {
        return new Dictionary<string, object>();
    }

    public override bool CanConvert(Type objectType)
    {
        // in addition to handling IDictionary<string, object>
        // we want to handle the deserialization of dict value
        // which is of type object
        return objectType == typeof(object) || base.CanConvert(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.StartObject
            || reader.TokenType == JsonToken.Null)
            return base.ReadJson(reader, objectType, existingValue, serializer);

        // if the next token is not an object
        // then fall back on standard deserializer (strings, numbers etc.)
        return serializer.Deserialize(reader);
    }
}

class Program
{
    static void Main(string[] args)
    {
        var json = File.ReadAllText(@"c:\test.json");
        var obj = JsonConvert.DeserializeObject<IDictionary<string, object>>(
            json, new JsonConverter[] {new MyConverter()});
    }
}

문서:Custom Creation Converter with Json.네트워크

이 Q를 접했을 때, 저는 매우 비슷하지만 조금 더 복잡한 요구를 가지고 있었습니다.처음에는 인정된 답변을 받아들일 수 있을 거라고 생각했지만, 조금 복잡해 보였고, 결국 다른 접근법을 택하게 되었습니다.레거시 C++ API 위에 현대적인 JSON 레이어를 추가하려고 했습니다.자세한 내용은 생략하고 요건은 다음과 같습니다.

  • JSON 오브젝트는Dictionary<string,object>.

  • JSON 어레이는List<object>.

  • JSON 값은 대응하는 프리미티브 CLR 값이 됩니다.

  • 개체와 배열을 무한히 중첩할 수 있습니다.

먼저 요청 문자열을 Newtonsoft JSON 개체로 역직렬화한 후 위의 요건에 따라 메서드를 호출하여 변환합니다.

var jsonObject = JsonConvert.DeserializeObject(requestString);
var apiRequest = ToApiRequest(jsonObject);
// call the legacy C++ API ...

API가 기대하는 구조로 변환하는 메서드는 다음과 같습니다.

    private static object ToApiRequest(object requestObject)
    {
        switch (requestObject)
        {
            case JObject jObject: // objects become Dictionary<string,object>
                return ((IEnumerable<KeyValuePair<string, JToken>>) jObject).ToDictionary(j => j.Key, j => ToApiRequest(j.Value));
            case JArray jArray: // arrays become List<object>
                return jArray.Select(ToApiRequest).ToList();
            case JValue jValue: // values just become the value
                return jValue.Value;
            default: // don't know what to do here
                throw new Exception($"Unsupported type: {requestObject.GetType()}");
        }
    }

나는 누군가가 이 접근법을 유용하게 여겨주길 바란다.

대체/업데이트:

나는 사전의 직역을 해제할 필요가 있었다.Strings 및 현재 Json을 사용합니다.NET(5.0) Custom Converter를 작성할 필요가 없고 (VB에서) 사용했을 뿐입니다.넷):

JsonConvert.DeserializeObject(Of IDictionary(Of String, IDictionary(Of String, String)))(jsonString)

또는 C#에서:

JsonConvert.DeserializeObject<IDictionary<String, IDictionary<String, String>>(jsonString);

C# 객체와 JSON 문자열 사이에서 시리얼화/비직렬화되는 "알 수 없는" 사전의 중첩/심층 구조를 가지고 있습니다.넷 5

사용하는 경우Newtonsoft자동으로 동작하지 않습니다.

사용하는 경우System.Text.Json자동으로 동작합니다.

//does NOT work (newtonDeserialized does not have the same data in the nested Dictionaries as in object):
var newtonSerialized = Newtonsoft.Json.JsonConvert.SerializeObject(object);
var newtonDeserialized = Newtonsoft.Json.JsonConvert.DeserializeObject<WaitlistResponse>(newtonSerialized);

//Works (netDeserialized have the same data in the nested Directories as in object):
var netSerialized = System.Text.Json.JsonSerializer.Serialize(object);
var netDeserialized = System.Text.Json.JsonSerializer.Deserialize<WaitlistResponse>(netSerialized);

@AlexD가 수용하는 솔루션은 json에 어레이가 있는 경우 이상적으로 동작하지 않습니다.a를 반환한다.JArrayJObject대신List<Dictionary<string, object>>

이 문제는 ReadJson() 메서드를 수정함으로써 해결할 수 있습니다.

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    if (reader.TokenType == JsonToken.StartObject || reader.TokenType == JsonToken.Null)
        return base.ReadJson(reader, objectType, existingValue, serializer);

    //if it's an array serialize it as a list of dictionaries
    if(reader.TokenType == JsonToken.ArrayStart)
        return serializer.Deserialize(reader, typeof(List<Dictionary<string, object>>));    


    // if the next token is not an object
    // then fall back on standard deserializer (strings, numbers etc.)
    return serializer.Deserialize(reader);
}

내 경우 모든 것이 중첩된 사전은 아니다.또한 기본 유형의 키 값인 배열이 있으며 배열의 개체가 사전이 아닌 경우 예외가 발생합니다.

그래서 Phillip S의 답변을 바탕으로 해서

public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue,
            JsonSerializer serializer)
        {
            if (reader.TokenType == JsonToken.StartObject || reader.TokenType == JsonToken.Null)
                return base.ReadJson(reader, objectType, existingValue, serializer);

            //if it's an array serialize it as a list of dictionaries
            if (reader.TokenType == JsonToken.StartArray)
            {
                return serializer.Deserialize(reader, typeof(List<object>)); // instead of List<Dictionary<string, object>>
            }

            // if the next token is not an object
            // then fall back on standard deserializer (strings, numbers etc.)
            return serializer.Deserialize(reader);
        }

아직 작동되지 않은 사람들에게 도움이 되길 바랍니다.

언급URL : https://stackoverflow.com/questions/6416017/json-net-deserializing-nested-dictionaries

반응형