programing

JSON.net: 기본 컨스트럭터를 사용하지 않고 역직렬화하려면 어떻게 해야 합니까?

procenter 2023. 2. 11. 17:29
반응형

JSON.net: 기본 컨스트럭터를 사용하지 않고 역직렬화하려면 어떻게 해야 합니까?

기본 생성자가 있는 클래스와 일련의 매개 변수를 사용하는 오버로드 생성자가 있습니다.이러한 매개 변수는 객체의 필드와 일치하며 구축 시 할당됩니다.현시점에서는 다른 용도로 디폴트 컨스트럭터가 필요하기 때문에 가능하면 유지하고 싶습니다.

하면 오브젝트는 로 문제없이 됩니다.기본 생성자를 제거하고 JSON 문자열을 전달하면 개체가 올바르게 직렬 해제되고 생성자 매개 변수를 문제 없이 전달합니다.결국 내가 예상했던 대로 오브젝트를 다시 채우게 된다.을 하면 " " "가 호출됩니다.JsonConvert.DeserializeObject<Result>(jsontext)속성이 더 이상 채워지지 않습니다.

에서 저는 덧셈을 .new JsonSerializerSettings(){CheckAdditionalContent = true}디시리얼라이제이션콜에 접속합니다.그건 아무 짓도 안 했어.

또 다른 주의: 컨스트럭터 파라미터는 소문자로 시작하는 것을 제외하고 필드 이름과 정확히 일치합니다.디폴트 컨스트럭터가 없어도 디시리얼라이제이션은 정상적으로 동작하기 때문에, 이것은 문제가 되지 않습니다.

다음은 제 컨스트럭터의 샘플입니다.

public Result() { }

public Result(int? code, string format, Dictionary<string, string> details = null)
{
    Code = code ?? ERROR_CODE;
    Format = format;

    if (details == null)
        Details = new Dictionary<string, string>();
    else
        Details = details;
}

Json.Net은 기본(파라미터가 없는) 생성자가 있는 경우 개체에 사용하는 것을 선호합니다.Json Jon Jon Jon Jon Jon Jon Jon.[] : [ Net ] : [ Net ]를 것을 는, 「Net 」를 추가할 수 있습니다.[JsonConstructor]존슨넷 투 콜

[JsonConstructor]
public Result(int? code, string format, Dictionary<string, string> details = null)
{
    ...
}

이것이 올바르게 동작하려면 컨스트럭터 파라미터명이 JSON 오브젝트의 대응하는 속성명(대소문자 무시)과 일치해야 합니다.은 아닙니다.JSON JSON. Net으로 된 속성)를 .[JsonProperty]구성 후 오브젝트를 채웁니다.

클래스에 속성을 추가하지 않거나 역직렬화하려는 클래스의 소스 코드를 제어하지 않는 경우, 또 다른 방법은 사용자 정의 JsonConverter를 생성하여 개체를 인스턴스화하고 채우는 것입니다.예를 들어 다음과 같습니다.

class ResultConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(Result));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // Load the JSON for the Result into a JObject
        JObject jo = JObject.Load(reader);

        // Read the properties which will be used as constructor parameters
        int? code = (int?)jo["Code"];
        string format = (string)jo["Format"];

        // Construct the Result object using the non-default constructor
        Result result = new Result(code, format);

        // (If anything else needs to be populated on the result object, do that here)

        // Return the result
        return result;
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

그런 다음 시리얼라이저 설정에 컨버터를 추가하고 시리얼라이즈를 해제할 때 설정을 사용합니다.

JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Converters.Add(new ResultConverter());
Result result = JsonConvert.DeserializeObject<Result>(jsontext, settings);

조금 늦었지만, 여기에서는 그다지 적합하지 않은 솔루션을 추가하겠습니다.왜냐하면질문은 이 솔루션과 중복되지 않았기 때문입니다.이 솔루션은 완전히 다른 솔루션이기 때문입니다.

난 일반적인 방법이 필요했어Json.NET사용자 정의 구조 유형에 대해 가장 구체적인 생성자를 선호하기 때문에JsonConstructor이러한 구조가 정의되는 프로젝트에 종속성을 추가하는 속성.

리버스 엔지니어링을 조금 실시하여 커스텀 계약 리졸버를 구현하여CreateObjectContractmethod를 사용하여 커스텀 작성 로직을 추가합니다.

public class CustomContractResolver : DefaultContractResolver {

    protected override JsonObjectContract CreateObjectContract(Type objectType)
    {
        var c = base.CreateObjectContract(objectType);
        if (!IsCustomStruct(objectType)) return c;

        IList<ConstructorInfo> list = objectType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).OrderBy(e => e.GetParameters().Length).ToList();
        var mostSpecific = list.LastOrDefault();
        if (mostSpecific != null)
        {
            c.OverrideCreator = CreateParameterizedConstructor(mostSpecific);
            c.CreatorParameters.AddRange(CreateConstructorParameters(mostSpecific, c.Properties));
        }

        return c;
    }

    protected virtual bool IsCustomStruct(Type objectType)
    {
        return objectType.IsValueType && !objectType.IsPrimitive && !objectType.IsEnum && !objectType.Namespace.IsNullOrEmpty() && !objectType.Namespace.StartsWith("System.");
    }

    private ObjectConstructor<object> CreateParameterizedConstructor(MethodBase method)
    {
        method.ThrowIfNull("method");
        var c = method as ConstructorInfo;
        if (c != null)
            return a => c.Invoke(a);
        return a => method.Invoke(null, a);
    }
}

이렇게 쓰고 있어요.

public struct Test {
  public readonly int A;
  public readonly string B;

  public Test(int a, string b) {
    A = a;
    B = b;
  }
}

var json = JsonConvert.SerializeObject(new Test(1, "Test"), new JsonSerializerSettings {
  ContractResolver = new CustomContractResolver()
});
var t = JsonConvert.DeserializeObject<Test>(json);
t.A.ShouldEqual(1);
t.B.ShouldEqual("Test");

여기 있는 몇 가지 답을 바탕으로, 저는 다음과 같은 글을 썼습니다.CustomConstructorResolver다른 사람에게 도움이 될 것 같아서요

다음과 같은 해결 메커니즘을 지원하며 모두 구성 가능합니다.

  • 속성을 표시하지 않고 하나의 개인 생성자를 정의할 수 있도록 단일 개인 생성자를 선택합니다.
  • 속성을 사용할 필요 없이 여러 개의 오버로드를 발생시킬 수 있도록 가장 구체적인 개인 생성자를 선택합니다.
  • Json에 종속되지 않고 기본 확인자와 같이 특정 이름의 속성이 표시된 생성자를 선택합니다.참조가 필요하기 때문에 넷 패키지Newtonsoft.Json.JsonConstructorAttribute.
public class CustomConstructorResolver : DefaultContractResolver
{
    public string ConstructorAttributeName { get; set; } = "JsonConstructorAttribute";
    public bool IgnoreAttributeConstructor { get; set; } = false;
    public bool IgnoreSinglePrivateConstructor { get; set; } = false;
    public bool IgnoreMostSpecificConstructor { get; set; } = false;

    protected override JsonObjectContract CreateObjectContract(Type objectType)
    {
        var contract = base.CreateObjectContract(objectType);

        // Use default contract for non-object types.
        if (objectType.IsPrimitive || objectType.IsEnum) return contract;

        // Look for constructor with attribute first, then single private, then most specific.
        var overrideConstructor = 
               (this.IgnoreAttributeConstructor ? null : GetAttributeConstructor(objectType)) 
            ?? (this.IgnoreSinglePrivateConstructor ? null : GetSinglePrivateConstructor(objectType)) 
            ?? (this.IgnoreMostSpecificConstructor ? null : GetMostSpecificConstructor(objectType));

        // Set override constructor if found, otherwise use default contract.
        if (overrideConstructor != null)
        {
            SetOverrideCreator(contract, overrideConstructor);
        }

        return contract;
    }

    private void SetOverrideCreator(JsonObjectContract contract, ConstructorInfo attributeConstructor)
    {
        contract.OverrideCreator = CreateParameterizedConstructor(attributeConstructor);
        contract.CreatorParameters.Clear();
        foreach (var constructorParameter in base.CreateConstructorParameters(attributeConstructor, contract.Properties))
        {
            contract.CreatorParameters.Add(constructorParameter);
        }
    }

    private ObjectConstructor<object> CreateParameterizedConstructor(MethodBase method)
    {
        var c = method as ConstructorInfo;
        if (c != null)
            return a => c.Invoke(a);
        return a => method.Invoke(null, a);
    }

    protected virtual ConstructorInfo GetAttributeConstructor(Type objectType)
    {
        var constructors = objectType
            .GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
            .Where(c => c.GetCustomAttributes().Any(a => a.GetType().Name == this.ConstructorAttributeName)).ToList();

        if (constructors.Count == 1) return constructors[0];
        if (constructors.Count > 1)
            throw new JsonException($"Multiple constructors with a {this.ConstructorAttributeName}.");

        return null;
    }

    protected virtual ConstructorInfo GetSinglePrivateConstructor(Type objectType)
    {
        var constructors = objectType
            .GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic);

        return constructors.Length == 1 ? constructors[0] : null;
    }

    protected virtual ConstructorInfo GetMostSpecificConstructor(Type objectType)
    {
        var constructors = objectType
            .GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
            .OrderBy(e => e.GetParameters().Length);

        var mostSpecific = constructors.LastOrDefault();
        return mostSpecific;
    }
}

다음은 XML 문서를 요지로 한 완전한 버전입니다.https://gist.github.com/bjorn-jarisch/80f77f4b6bdce3b434b0f7a1d06baa95

피드백 감사합니다.

Newtonsoft의 기본 동작입니다.Json은 그 사람을 찾으러 간다.public컨스트럭터기본 생성자가 클래스 또는 동일한 어셈블리를 포함하는 데만 사용되는 경우 액세스 수준을 다음과 같이 줄일 수 있습니다.protected ★★★★★★★★★★★★★★★★★」internalJson Json을 선택합니다.public컨스트럭터

물론 이 솔루션은 특정 사례에 한정되어 있습니다.

internal Result() { }

public Result(int? code, string format, Dictionary<string, string> details = null)
{
    Code = code ?? ERROR_CODE;
    Format = format;

    if (details == null)
        Details = new Dictionary<string, string>();
    else
        Details = details;
}

Zoltan의 답변을 바탕으로 시그니처에 따라 특정 컨스트럭터를 사용할 수 있는 바리에이션을 만들었습니다.

사용.

return new JsonSerializerSettings
{
    ContractResolver = new DynamicObjectResolver(t =>
    {
        if (t == typeof(QueueProperties))
            return new Type[] { typeof(string) };

        return null;
    })
};

다음은 구현입니다.

using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Concurrent;
using System.Reflection;

namespace ConsoleApp76.Json
{
    class DynamicObjectResolver : DefaultContractResolver
    {
        private readonly Func<Type, Type[]> GetConstructorSignature;
        private readonly ConcurrentDictionary<Type, ConstructorInfo> TypeToConstructorLookup =
            new ConcurrentDictionary<Type, ConstructorInfo>();

        public DynamicObjectResolver(Func<Type, Type[]> getConstructorSignature)
        {
            if (getConstructorSignature is null)
                throw new ArgumentNullException(nameof(getConstructorSignature));
            GetConstructorSignature = getConstructorSignature;
        }

        protected override JsonObjectContract CreateObjectContract(Type objectType)
        {
            var result = base.CreateObjectContract(objectType);
            ConstructorInfo constructor = TypeToConstructorLookup.GetOrAdd(objectType, t => FindConstructorInfo(t));
            if (constructor is null)
                return result;

            result.OverrideCreator = CreateParameterizedConstructor(constructor);
            foreach (var param in CreateConstructorParameters(constructor, result.Properties))
                result.CreatorParameters.Add(param);

            return result;
        }

        private ConstructorInfo FindConstructorInfo(Type objectType)
        {
            Type[] constructorSignature = GetConstructorSignature(objectType);
            if (constructorSignature is null)
                return null;

            return objectType.GetConstructor(
                bindingAttr:
                    System.Reflection.BindingFlags.Public
                    | System.Reflection.BindingFlags.NonPublic
                    | System.Reflection.BindingFlags.Instance,
                binder: null,
                types: new Type[] { typeof(string) },
                modifiers: null);
        }

        private static ObjectConstructor<object> CreateParameterizedConstructor(MethodBase method)
        {
            if (method is null)
                throw new ArgumentNullException(nameof(method));

            var c = method as ConstructorInfo;
            if (c != null)
                return a => c.Invoke(a);
            return a => method.Invoke(null, a);
        }
    }
}

솔루션:

public Response Get(string jsonData) {
    var json = JsonConvert.DeserializeObject<modelname>(jsonData);
    var data = StoredProcedure.procedureName(json.Parameter, json.Parameter, json.Parameter, json.Parameter);
    return data;
}

모델:

public class modelname {
    public long parameter{ get; set; }
    public int parameter{ get; set; }
    public int parameter{ get; set; }
    public string parameter{ get; set; }
}

언급URL : https://stackoverflow.com/questions/23017716/json-net-how-to-deserialize-without-using-the-default-constructor

반응형