当前位置: 首页 > 知识库问答 >
问题:

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

尹凌龙
2023-03-14

我有一个类,它有一个默认构造函数,还有一个重载构造函数,它接受一组参数。这些参数与对象上的字段匹配,并在构造时指定。此时,我需要用于其他目的的默认构造函数,因此如果可以的话,我希望保留它。

我的问题:如果我删除默认的构造函数并传递JSON字符串,对象反序列化正确,并传递构造函数参数没有任何问题。我最终以我所期望的方式找回填充的对象。但是,一旦我将默认构造函数添加到对象中,当我调用JsonConverts时。对象

在这一点上,我已经尝试添加新的JsonSeriazerSetts(){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;
}

共有3个答案

郭浩穰
2023-03-14

根据这里的一些答案,我编写了一个CustomConstructorResolver,用于当前项目,我认为它可能会帮助其他人。

它支持以下可配置的解析机制:

  • 选择一个私有构造函数,这样就可以定义一个私有构造函数,而不必用属性标记它
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/maverickelementalch/80f77f4b6bdce3b434b0f7a1d06baa95

感谢您的反馈。

通典
2023-03-14
匿名用户

有点晚了,不太适合这里,但是我要在这里添加我的解决方案,因为我的问题已经作为这个问题的副本关闭了,因为这个解决方案完全不同。

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

我已经逆向工程了一点,并实现了一个自定义合同解析器,在那里我重写了CreateObject合同方法来添加我的自定义创建逻辑。

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

丁宏盛
2023-03-14

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

[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);
 类似资料:
  • 问题内容: 我想使用com.fasterxml.jackson.databind.ObjectMapper序列化和反序列化不可变对象。 不可变类如下所示(只有3个内部属性,获取器和构造函数): 但是,当我运行此单元测试时: 我得到这个例外: 这个异常要求我创建一个默认的构造函数,但这是一个不可变的对象,所以我不想拥有它。它将如何设置内部属性?这会完全混淆API的用户。 所以我的问题是: 我可以在没

  • 我想知道为什么Spring boot可以反序列化没有默认构造函数的类由Jackson的ObjectMapper,但是当我在单元测试中手动使用ObjectMapper时,它不能反序列化(com.fasterxml.jackson.databind.exc.Invalid定义异常:不能构造实例的(没有创建者,像默认构造函数,存在):不能反序列化从对象值(没有基于委托或属性的创建者))。 这是我的控制器

  • 我使用了Spring Boot 1.4.3。release和bean能够从JSON反序列化这样的对象。 升级到Spring Boot 2.0.0.M7后,我收到以下异常: 那么,为什么它在Spring Boot1.4.3中工作,而在Spring Boot2中失败呢?是否可以将bean配置为与旧版本相同的行为方式?

  • 当我们的团队处理关于C代码中POD的单元化成员的valgrind警告时,我想到了这个有趣的答案: https://stackoverflow.com/a/5914697/629530 重申要点,考虑C中的以下POD结构: 构造C类型对象的以下调用调用默认构造函数,成员使用该默认构造函数初始化(再次复制Martin York答案中的代码和注释): 有道理。Martin York继续指出,使用以下声明

  • 我有一个四叉树,我想在几个不同的机器上使用它。调用构造函数(即构建树)需要很长时间。我不想每次我需要使用它的时候都建这棵树。 我正在寻找一种方法,将我的树持久保存在硬盘中,发送到每个节点,然后快速将其加载到内存中,以便执行查找。 序列化能帮助我吗?我知道我可以序列化树,把它保存到磁盘上,然后反序列化(这就是我对序列化的全部了解)。据我所知,反序列化步骤需要一个默认的构造函数来构建树。因为构建树的计

  • 问题内容: 所有, 我正在尝试在一些古老的Java代码中进行一些单元测试(无接口,无抽象等)。 这是一个使用ServletContext的servlet(我假设它是由Tomcat设置的),并且它的数据库信息在web.xml / context.xml文件中设置。现在,我已经弄清楚了如何制作Fake ServletContext,但是代码已经 遍布整个地方(因此替换它是不可行的)。我需要找到一种方法