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;
}
158782 次浏览

杰森。如果对象存在缺省(无参数)构造函数,Net 倾向于对其使用缺省(无参数)构造函数。如果有多个构造函数,并且需要 Json。Net 使用一个非默认的,然后您可以将 [JsonConstructor]属性添加到您想要 Json 的构造函数中。网络呼叫。

[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属性,它将为定义每个这样的结构类型的项目添加一个依赖项。

我已经反向工程了一点,并实现了一个自定义契约解析器,其中我重写了 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);
}
}

我就是这么用的。

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。Net 包,因为您需要引用 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构造函数。如果你的缺省构造函数只用于包含类或同一个程序集,你可以把访问级别降低到 protectedinternal,这样牛顿软件。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;
}

解决方案:

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

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