我的问题:如果删除默认构造函数并传入JSON字符串,则该对象将正确反序列化并传入构造函数参数,而不会出现任何问题。我最终以我期望的方式取回了对象。但是,一旦将默认构造函数添加到对象中,当我调用
JsonConvert.DeserializeObject<Result>(jsontext)
时,将不再填充属性。此时,我尝试将
new JsonSerializerSettings(){CheckAdditionalContent = true}
添加到反序列化调用中。那什么也没做。 另一个说明。除了参数以小写字母开头外,contructor参数确实与字段名称完全匹配。我认为这并不重要,因为就像我提到的那样,反序列化可以在没有默认构造函数的情况下正常工作。
这是我的构造函数的一个示例:
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;
}
#1 楼
如果有一个对象,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);
评论
这工作了。我现在不得不在我的模型项目中采用JSON.net依赖,这有点糟,但是嘿。我将其标记为答案。
–kmacdonald
14年4月11日在16:35
还有其他选项-您可以为您的类创建一个自定义JsonConverter。这将消除依赖关系,但随后您必须自己在转换器中处理实例化和填充对象。也可能编写一个定制的ContractResolver,通过更改其JsonObjectContract来指示Json.Net使用其他构造函数,但这可能听起来有些棘手。
–布莱恩·罗杰斯(Brian Rogers)
2014年4月11日在16:59
是的,我认为该属性可以正常工作。反序列化调用实际上是通用的,因此它可以是任何类型的对象。我认为您的原始答案会很好。谢谢(你的)信息!
–kmacdonald
2014年4月11日在17:17
如果可以为构造函数选择设置另一个约定,那将真的有帮助。例如,我认为Unity容器支持这一点。然后,您可以使它始终选择具有最多参数的构造函数,而不是退回到默认参数。 Json.Net是否存在这种扩展点的可能性?
– julealgon
15年4月10日在15:08
不要忘记使用Newtonsoft.Json;
–布鲁诺·比耶里(Bruno Bieri)
17年1月20日在14:07
#2 楼
有点晚了,并不完全适合这里,但是我要在这里添加我的解决方案,因为我的问题已经作为该问题的副本被关闭,并且因为该解决方案完全不同。我需要一种通用的方法来指示
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");
评论
我目前正在使用上面接受的答案,但也要感谢您展示您的解决方案!
–DotBert
16-3-31在9:46
我删除了对结构的限制(检查objectType.IsValueType),这很好用,谢谢!
– Alex Angas
17年9月9日在16:03
@AlexAngas是的,总体上采用此策略确实很有意义,谢谢您的反馈。
–佐尔坦·塔玛斯(ZoltánTamási)
17年9月10日在7:27
#3 楼
基于这里的一些答案,我编写了一个在当前项目中使用的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/maverickelementalch/80f77f4b6bdce3b434b0f7a1d06baa95
反馈意见。
评论
很好的解决方案!感谢分享。
– thomai
3月9日8:30
#4 楼
Newtonsoft.Json的默认行为是查找public
构造函数。如果默认构造函数仅用于包含类或同一程序集,则可以将访问级别降低到protected
或internal
,以便Newtonsoft.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;
}
#5 楼
解决方案: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; }
}
评论
也许这可以帮助stackoverflow.com/questions/8254503/…Dupe:无法通过json-net与多个构造函数序列化类