当JsonConstructor参数名称与JSON不匹配时,如何引发异常? [英] How to throw an exception when JsonConstructor parameter name doesn't match JSON?
问题描述
我正在反序列化一堆C#只读结构(它们的构造函数用[JsonConstructor]
标记),并且如果接收到的JSON格式错误,我想尽早失败.
I'm deserializing a bunch of C# readonly structures (which have their constructors marked by [JsonConstructor]
), and I'm trying to fail early if any JSON that I receive is malformed.
不幸的是,如果构造函数参数与输入的JSON之间存在命名差异,则只会为该参数分配一个默认值.有没有一种方法可以让我获得例外,因此这些默认值不会意外地污染"请求.我其余的业务逻辑?我试过玩各种JsonSerializerSettings
,但无济于事.
Unfortunately, if there is a naming discrepancy between the constructor parameter and the input JSON, the parameter just gets assigned a default value. Is there a way that I could get an exception instead, so these defaults don't accidentally "pollute" the rest of my business logic? I have tried playing with various JsonSerializerSettings
but to no avail.
简化示例:
public readonly struct Foo {
[JsonConstructor]
public Foo(long wrong) {
FooField = wrong;
}
public readonly long FooField;
}
public void JsonConstructorParameterTest() {
// The Foo constructor parameter name ("wrong") doesn't match the JSON property name ("FooField").
var foo = JsonConvert.DeserializeObject<Foo>("{\"FooField\":42}");
// The foo.FooField is now 0.
// How can we cause the above to throw an exception instead of just assigning 0 to Foo.FooField?
}
可以通过将wrong
重命名为fooField
来解决上述问题,但是我想知道在将0提交到我的数据库之前.
The above can be fixed by renaming wrong
into fooField
, but I'd like to know that before 0 has already been committed to my database.
推荐答案
合同解析器,来自此答案到 JSON.net不应将默认值用于构造函数参数,应将默认值用于属性 几乎可以完成您的工作想要,但是它有一个明显的限制:
The contract resolver from this answer to JSON.net should not use default values for constructor parameters, should use default for properties almost does what you want, however it has a noted restriction:
这仅在有相应属性的情况下有效. 似乎没有一种简单的方法可以根据需要标记没有相应属性的构造函数参数.
由于标记了不匹配"的内容,所需的构造函数参数似乎不起作用(演示小提琴#1 此处),您可以从以下位置修改合同解析器:如果找到不匹配的构造函数参数,该答案将在合同构建期间抛出异常.
Since marking an "unmatched" constructor parameter as required doesn't seem to work (demo fiddle #1 here) you can modify the contract resolver from that answer to throw an exception during contract construction if an unmatched constructor parameter is found.
以下合同解析器会执行此操作:
The following contract resolver does this:
public class ConstructorParametersRequiredContractResolver : DefaultContractResolver
{
protected override JsonProperty CreatePropertyFromConstructorParameter(JsonProperty matchingMemberProperty, ParameterInfo parameterInfo)
{
// All constructor parameters are required to have some matching member.
if (matchingMemberProperty == null)
throw new JsonSerializationException(string.Format("No matching member for constructor parameter \"{0}\" of type \"{1}\".", parameterInfo, parameterInfo.Member.DeclaringType));
var property = base.CreatePropertyFromConstructorParameter(matchingMemberProperty, parameterInfo);
if (property != null && matchingMemberProperty != null)
{
if (!matchingMemberProperty.IsRequiredSpecified) // If the member is already explicitly marked with some Required attribute, don't override it.
{
Required required;
if (matchingMemberProperty.PropertyType != null && (matchingMemberProperty.PropertyType.IsValueType && Nullable.GetUnderlyingType(matchingMemberProperty.PropertyType) == null))
{
required = Required.Always;
}
else
{
required = Required.AllowNull;
}
// It turns out to be necessary to mark the original matchingMemberProperty as required.
property.Required = matchingMemberProperty.Required = required;
}
}
return property;
}
}
要使用它,请构造解析器:
To use it, construct the resolver:
static IContractResolver resolver = new ConstructorParametersRequiredContractResolver();
和单元测试如下:
var settings = new JsonSerializerSettings
{
ContractResolver = resolver,
};
JsonConvert.DeserializeObject<Foo>("{\"FooField\":42}", settings);
请注意,您可能需要缓存并重用合同解析器以达到最佳效果性能.
演示小提琴#2 此处.
这篇关于当JsonConstructor参数名称与JSON不匹配时,如何引发异常?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!