programing

알려진 필드와 알 수 없는 필드로 json 직렬화 해제

closeapi 2023. 3. 2. 22:16
반응형

알려진 필드와 알 수 없는 필드로 json 직렬화 해제

다음과 같은 json 결과가 지정됩니다.기본 json 결과에는 알려진 필드 집합이 있습니다.

{
    "id": "7908",
    "name": "product name"
}

, 추가 예에서는 " )로 할 수 ._unknown_field_name_1 ★★★★★★★★★★★★★★★★★」_unknown_field_name_2이데올로기 때문에

{
    "id": "7908",
    "name": "product name",
    "_unknown_field_name_1": "some value",
    "_unknown_field_name_2": "some value"
}

json 결과를 기존 필드의 속성이 있는 클래스와 직렬화 및 직렬화 해제하고, 사전과 같은 속성(또는 여러 속성)에 매핑하여 액세스 및 수정할 수 있도록 합니다.

public class Product
{
    public string id { get; set; }
    public string name { get; set; }
    public Dictionary<string, string> fields { get; set; }
}

json serializer에 접속하여 누락된 멤버의 매핑을 직접 수행할 수 있는 방법이 필요하다고 생각합니다(시리얼라이즈 및 디시리얼라이즈 모두).저는 다양한 가능성을 모색해 왔습니다.

  • json.net 및 커스텀 계약 해결사(방법을 알 수 없음)
  • datacontract serializer(온시리얼라이즈, 온시리얼라이즈만 덮어쓸 수 있습니다)
  • 다이나믹에 시리얼화하여 커스텀 매핑을 실행한다(이 방법은 동작할 수 있지만 많은 작업이 필요한 것 같다).
  • 제품을 DynamicObject에서 상속할 수 있도록 합니다(시리얼라이저는 리플렉션으로 동작하며 trygetmember 메서드와 trysetmember 메서드를 호출하지 않습니다).

레스트샤프를 사용하지만 시리얼라이저는 꽂을 수 있습니다.

아, 그리고 난 json 결과를 바꿀 수 없고 이것이나 이것 또한 나에게 도움이 되지 않았다.

업데이트: 더 비슷해 보입니다.http://geekswithblogs.net/DavidHoerster/archive/2011/07/26/json.net-custom-convertersndasha-quick-tour.aspx

이 문제를 해결하는 보다 쉬운 방법은 JSON의 JsonExtensionDataAttribute를 사용하는 것입니다.그물

public class MyClass
{
   // known field
   public decimal TaxRate { get; set; }

   // extra fields
   [JsonExtensionData]
   private IDictionary<string, JToken> _extraStuff;
}

여기 프로젝트 블로그에 샘플이 있습니다.

업데이트 여기에는 JSON이 필요합니다.NET v5 릴리즈 5 이후

https://gist.github.com/LodewijkSioen/5101814 를 참조해 주세요.

당신이 찾고 있던 것은 관습이었다.

나는 별로 좋아하지 않지만, 이렇게 하면 해결할 수 있다.Newton/J를 사용하여 해결했습니다.SON.Net.Json Converter를 역직렬화에도 사용할 수 있을 것 같습니다.

private const string Json = "{\"id\":7908,\"name\":\"product name\",\"_unknown_field_name_1\":\"some value\",\"_unknown_field_name_2\":\"some value\"}";

    [TestMethod]
    public void TestDeserializeUnknownMembers()
    {
        var @object = JObject.Parse(Json);

        var serializer = new Newtonsoft.Json.JsonSerializer();
        serializer.MissingMemberHandling = Newtonsoft.Json.MissingMemberHandling.Error;
        serializer.Error += (sender, eventArgs) =>
            {
                var contract = eventArgs.CurrentObject as Contract ?? new Contract();
                contract.UnknownValues.Add(eventArgs.ErrorContext.Member.ToString(), @object[eventArgs.ErrorContext.Member.ToString()].Value<string>());
                eventArgs.ErrorContext.Handled = true;
            };

        using (MemoryStream memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(Json)))
        using (StreamReader streamReader = new StreamReader(memoryStream))
        using (JsonReader jsonReader = new JsonTextReader(streamReader))
        {
            var result = serializer.Deserialize<Contract>(jsonReader);
            Assert.IsTrue(result.UnknownValues.ContainsKey("_unknown_field_name_1"));
            Assert.IsTrue(result.UnknownValues.ContainsKey("_unknown_field_name_2"));
        }
    }

    [TestMethod]
    public void TestSerializeUnknownMembers()
    {
        var deserializedObject = new Contract
        {
            id = 7908,
            name = "product name",
            UnknownValues = new Dictionary<string, string>
        {
            {"_unknown_field_name_1", "some value"},
            {"_unknown_field_name_2", "some value"}
        }
        };

        var json = JsonConvert.SerializeObject(deserializedObject, new DictionaryConverter());
        Console.WriteLine(Json);
        Console.WriteLine(json);
        Assert.AreEqual(Json, json);
    }
}

class DictionaryConverter : JsonConverter
{
    public DictionaryConverter()
    {

    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(Contract);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var contract = value as Contract;
        var json = JsonConvert.SerializeObject(value);
        var dictArray = String.Join(",", contract.UnknownValues.Select(pair => "\"" + pair.Key + "\":\"" + pair.Value + "\""));

        json = json.Substring(0, json.Length - 1) + "," + dictArray + "}";
        writer.WriteRaw(json);
    }
}

class Contract
{
    public Contract()
    {
        UnknownValues = new Dictionary<string, string>();
    }

    public int id { get; set; }
    public string name { get; set; }

    [JsonIgnore]
    public Dictionary<string, string> UnknownValues { get; set; }
}
}

최근에 비슷한 문제가 있어서 출사표를 던져야겠다고 생각했어요.다음으로 역직렬화하려는 JSON의 예를 나타냅니다.

{
    "agencyId": "agency1",
    "overrides": {
        "assumption.discount.rates": "value: 0.07",
        ".plan": {
            "plan1": {
                "assumption.payroll.growth": "value: 0.03",
                "provision.eeContrib.rate": "value: 0.35"
            },
            "plan2": {
                ".classAndTier": {
                    "misc:tier1": {
                        "provision.eeContrib.rate": "value: 0.4"
                    },
                    "misc:tier2": {
                        "provision.eeContrib.rate": "value: 0.375"
                    }
                }
            }
        }
    }
}

이는 오버라이드가 다른 수준에서 적용되어 트리로 상속되는 시스템을 위한 것입니다.어쨌든, 내가 원했던 데이터 모델은, 이러한 특별한 상속 룰이 있는 부동산 백을 가질 수 있는 것이었습니다.

그 결과 다음과 같은 결과가 나왔습니다.

public class TestDataModel
{
    public string AgencyId;
    public int Years;
    public PropertyBagModel Overrides;
}

public class ParticipantFilterModel
{
    public string[] ClassAndTier;
    public string[] BargainingUnit;
    public string[] Department;
}

public class PropertyBagModel
{
    [JsonExtensionData]
    private readonly Dictionary<string, JToken> _extensionData = new Dictionary<string, JToken>();

    [JsonIgnore]
    public readonly Dictionary<string, string> Values = new Dictionary<string, string>();

    [JsonProperty(".plan", NullValueHandling = NullValueHandling.Ignore)]
    public Dictionary<string, PropertyBagModel> ByPlan;

    [JsonProperty(".classAndTier", NullValueHandling = NullValueHandling.Ignore)]
    public Dictionary<string, PropertyBagModel> ByClassAndTier;

    [JsonProperty(".bargainingUnit", NullValueHandling = NullValueHandling.Ignore)]
    public Dictionary<string, PropertyBagModel> ByBarginingUnit;

    [OnSerializing]
    private void OnSerializing(StreamingContext context)
    {
        foreach (var kvp in Values)
            _extensionData.Add(kvp.Key, kvp.Value);
    }

    [OnSerialized]
    private void OnSerialized(StreamingContext context)
    {
        _extensionData.Clear();
    }

    [OnDeserialized]
    private void OnDeserialized(StreamingContext context)
    {
        Values.Clear();
        foreach (var kvp in _extensionData.Where(x => x.Value.Type == JTokenType.String))
            Values.Add(kvp.Key, kvp.Value.Value<string>());
        _extensionData.Clear();
    }
}

기본적인 생각은 다음과 같습니다.

  1. JSON에 의한 역직렬화에 관한 PropertyBagModel.NET 에는 [ByPlan], [ByClassAndTier]등의 필드가 입력되어 프라이빗 _extensionData 필드도 입력되어 있습니다.
  2. 그러면 JSON.NET은 프라이빗 OnDeserialized() 메서드를 호출하여 필요에 따라 데이터를 _extensionData에서 Values로 이동합니다(그렇지 않으면 바닥에 떨어뜨립니다).알고 싶은 것이 있으면 로그에 기록할 수 있습니다.그런 다음 메모리를 소비하지 않도록 _extensionData에서 여분의 gunk를 제거합니다.
  3. 시리얼라이제이션 시 OnSerializing 메서드는 데이터를 _extensionData로 이동하는 콜을 수신하여 저장합니다.
  4. 시리얼화가 완료되면 OnSerialized가 호출되고 _extensionData에서 추가 정보가 제거됩니다.

필요에 따라 _extensionDataDictionary를 추가로 삭제하고 다시 만들 수 있지만, 저는 이러한 오브젝트를 많이 사용하지 않기 때문에 실제적인 가치를 찾을 수 없었습니다.이를 위해서는 OnSerializing에서 만들고 OnSerialized에서 삭제하기만 하면 됩니다.OnDeserializing에서는 클리어하지 않고 해방할 수 있습니다.

비슷한 문제를 조사하다가 이 게시물을 발견했습니다.

여기 성찰하는 방법이 있습니다.

보다 범용적으로 하기 위해서는 단순히 propertyInfo에서 ToString()을 사용하는 것이 아니라 속성의 유형을 확인해야 합니다.SetValue(OFC가 아닌 한 모든 실제 속성이 문자열입니다).

또한 소문자 속성 이름은 C#에서는 표준이 아니지만 GetProperty가 대소문자를 구분하기 때문에 다른 옵션은 거의 없습니다.

public class Product
{
    private Type _type;

    public Product()
    {
        fields = new Dictionary<string, object>();
        _type = GetType();
    }

    public string id { get; set; }
    public string name { get; set; }
    public Dictionary<string, object> fields { get; set; }

    public void SetProperty(string key, object value)
    {
        var propertyInfo = _type.GetProperty(key);
        if (null == propertyInfo)
        {
            fields.Add(key,value);
            return;
        }
        propertyInfo.SetValue(this, value.ToString());
    }
}
...
private const string JsonTest = "{\"id\":7908,\"name\":\"product name\",\"_unknown_field_name_1\":\"some value\",\"_unknown_field_name_2\":\"some value\"}";

var product = new Product();
var data = JObject.Parse(JsonTest);
foreach (var item in data)
{
    product.SetProperty(item.Key, item.Value);
}

언급URL : https://stackoverflow.com/questions/15253875/deserialize-json-with-known-and-unknown-fields

반응형