如何在不使用默认构造函数的情况下反序列化?

时间:2021-12-08 17:02:46

I have a class that has a default constructor and also an overloaded constructor that takes in a set of parameters. These parameters match to fields on the object and are assigned on construction. At this point i need the default constructor for other purposes so i would like to keep it if i can.

我有一个具有默认构造函数和重载构造函数的类,该构造函数接受一组参数。这些参数与对象上的字段匹配,并在构造时分配。此时,我需要其他用途的默认构造函数,所以如果可以,我希望保留它。

My Problem: If I remove the default constructor and pass in the JSON string, the object deserializes correctly and passes in the constructor parameters without any issues. I end up getting back the object populated the way I would expect. However, as soon as I add the default constructor into the object, when i call JsonConvert.DeserializeObject<Result>(jsontext) the properties are no longer populated.

我的问题是:如果我删除默认构造函数并传入JSON字符串,那么对象将正确反序列化,并在没有任何问题的情况下传入构造函数参数。我以我期望的方式返回填充对象。但是,当我将默认构造函数添加到对象中时,当我调用JsonConvert.DeserializeObject (jsontext)时,属性不再被填充。

At this point I have tried adding new JsonSerializerSettings(){CheckAdditionalContent = true} to the deserialization call. that did not do anything.

此时,我尝试向反序列化调用添加新的jsonaliserizersettings (){CheckAdditionalContent = true}。它什么也没做。

Another note. the contructor parameters do match the names of the fields exactly except that the parameters are start with a lowercase letter. I wouldn't think this would matter since, like i mentioned, the deserialization works fine with no default constructor.

另一个注意。contructor参数确实匹配字段的名称,只是参数以小写字母开头。我不认为这很重要,因为,正如我提到的,反序列化工作很好,没有默认构造函数。

Here is a sample of my constructors:

这是我的施工人员的一个例子:

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

2 个解决方案

#1


124  

Json.Net prefers to use the default (parameterless) constructor on an object if there is one. If there are multiple constructors and you want Json.Net to use a non-default one, then you can add the [JsonConstructor] attribute to the constructor that you want Json.Net to call.

Json。Net更喜欢在对象上使用默认的(无参数的)构造函数。如果有多个构造函数,需要Json。Net使用非默认属性,然后可以将[JsonConstructor]属性添加到需要Json的构造函数中。网络电话。

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

It is important that the constructor parameter names match the corresponding property names of the JSON object (ignoring case) for this to work correctly. You do not necessarily have to have a constructor parameter for every property of the object, however. For those JSON object properties that are not covered by the constructor parameters, Json.Net will try to use the public property accessors (or properties/fields marked with [JsonProperty]) to populate the object after constructing it.

重要的是,构造函数参数名称要与JSON对象的相应属性名称(忽略大小写)匹配,才能使其正确工作。不过,您不必为对象的每个属性都设置构造函数参数。对于构造函数参数不包含的JSON对象属性,JSON。Net将尝试使用公共属性访问器(或标有[JsonProperty]的属性/字段)在构造对象之后填充该对象。

If you do not want to add attributes to your class or don't otherwise control the source code for the class you are trying to deserialize, then another alternative is to create a custom JsonConverter to instantiate and populate your object. For example:

如果不希望向类添加属性,或者不控制要反序列化的类的源代码,那么另一种方法是创建自定义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();
    }
}

Then, add the converter to your serializer settings, and use the settings when you deserialize:

然后,将转换器添加到序列化设置中,反序列化时使用这些设置:

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

#2


19  

A bit late and not exactly suited here, but I'm gonna add my solution here, because my question had been closed as a duplicate of this one, and because this solution is completely different.

有点晚了,不太适合这里,但我要在这里添加我的解,因为我的问题是这个的重复,因为这个解是完全不同的。

I needed a general way to instruct Json.NET to prefer the most specific constructor for a user defined struct type, so I can omit the JsonConstructor attributes which would add a dependency to the project where each such struct is defined.

我需要一种指导Json的通用方法。NET为用户定义的struct类型选择最特定的构造函数,因此我可以省略JsonConstructor属性,它将向定义每个此类结构的项目添加依赖项。

I've reverse engineered a bit and implemented a custom contract resolver where I've overridden the CreateObjectContract method to add my custom creation logic.

我反向设计了一点,并实现了一个自定义契约解析器,其中重写了CreateObjectContract方法以添加自定义创建逻辑。

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

I'm using it like this.

我是这样用的。

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

#1


124  

Json.Net prefers to use the default (parameterless) constructor on an object if there is one. If there are multiple constructors and you want Json.Net to use a non-default one, then you can add the [JsonConstructor] attribute to the constructor that you want Json.Net to call.

Json。Net更喜欢在对象上使用默认的(无参数的)构造函数。如果有多个构造函数,需要Json。Net使用非默认属性,然后可以将[JsonConstructor]属性添加到需要Json的构造函数中。网络电话。

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

It is important that the constructor parameter names match the corresponding property names of the JSON object (ignoring case) for this to work correctly. You do not necessarily have to have a constructor parameter for every property of the object, however. For those JSON object properties that are not covered by the constructor parameters, Json.Net will try to use the public property accessors (or properties/fields marked with [JsonProperty]) to populate the object after constructing it.

重要的是,构造函数参数名称要与JSON对象的相应属性名称(忽略大小写)匹配,才能使其正确工作。不过,您不必为对象的每个属性都设置构造函数参数。对于构造函数参数不包含的JSON对象属性,JSON。Net将尝试使用公共属性访问器(或标有[JsonProperty]的属性/字段)在构造对象之后填充该对象。

If you do not want to add attributes to your class or don't otherwise control the source code for the class you are trying to deserialize, then another alternative is to create a custom JsonConverter to instantiate and populate your object. For example:

如果不希望向类添加属性,或者不控制要反序列化的类的源代码,那么另一种方法是创建自定义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();
    }
}

Then, add the converter to your serializer settings, and use the settings when you deserialize:

然后,将转换器添加到序列化设置中,反序列化时使用这些设置:

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

#2


19  

A bit late and not exactly suited here, but I'm gonna add my solution here, because my question had been closed as a duplicate of this one, and because this solution is completely different.

有点晚了,不太适合这里,但我要在这里添加我的解,因为我的问题是这个的重复,因为这个解是完全不同的。

I needed a general way to instruct Json.NET to prefer the most specific constructor for a user defined struct type, so I can omit the JsonConstructor attributes which would add a dependency to the project where each such struct is defined.

我需要一种指导Json的通用方法。NET为用户定义的struct类型选择最特定的构造函数,因此我可以省略JsonConstructor属性,它将向定义每个此类结构的项目添加依赖项。

I've reverse engineered a bit and implemented a custom contract resolver where I've overridden the CreateObjectContract method to add my custom creation logic.

我反向设计了一点,并实现了一个自定义契约解析器,其中重写了CreateObjectContract方法以添加自定义创建逻辑。

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

I'm using it like this.

我是这样用的。

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