kma*_*ald 117 c# json 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;
}
Run Code Online (Sandbox Code Playgroud)
Bri*_*ers 180
Json.Net倾向于在对象上使用默认(无参数)构造函数(如果有).如果有多个构造函数并且您希望Json.Net使用非默认[JsonConstructor]构造函数,那么您可以将该属性添加到您希望Json.Net调用的构造函数中.
[JsonConstructor]
public Result(int? code, string format, Dictionary<string, string> details = null)
{
...
}
Run Code Online (Sandbox Code Playgroud)
重要的是构造函数参数名称与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();
}
}
Run Code Online (Sandbox Code Playgroud)
然后,将转换器添加到序列化器设置,并在反序列化时使用设置:
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Converters.Add(new ResultConverter());
Result result = JsonConvert.DeserializeObject<Result>(jsontext, settings);
Run Code Online (Sandbox Code Playgroud)
Zol*_*ási 32
有点晚了,并不完全适合这里,但我会在这里添加我的解决方案,因为我的问题已被关闭作为这个问题的副本,因为这个解决方案是完全不同的.
我需要一种通用的方法来指示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);
}
}
Run Code Online (Sandbox Code Playgroud)
我这样用它.
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");
Run Code Online (Sandbox Code Playgroud)
根据这里的一些答案,我编写了一个CustomConstructorResolver用于当前项目的文件,我认为它可能对其他人有帮助。
它支持以下解析机制,全部可配置:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
以下是以 XML 文档为要点的完整版本: https://gist.github.com/bjorn-jarisch/80f77f4b6bdce3b434b0f7a1d06baa95
感谢反馈。