在默认设置中更改 ContractResolver 后自定义 JsonConverter 失败
Custom JsonConverter is failing after changing the ContractResolver in the default settings
我为 NewtonSoft 制作了一个自定义的 JsonConvert JSON...经过全面测试,现在可以正常工作一段时间了。但是有人说属性需要驼峰式而不是 PascalCased。
所以,好的...我更改了默认设置中的 NamingStrategy。
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
DateFormatHandling = DateFormatHandling.IsoDateFormat,
DateTimeZoneHandling = DateTimeZoneHandling.Utc,
Converters = new List<JsonConverter>
{
new StringEnumConverter(),
new MessageEnvelopJsonConverter(PayloadTypes.GetMappings())
},
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
}
};
果然,输出工作正常......这并不奇怪;转换器仅使用读取来确保接口的正确实现属性,但没有写入实现。
现在获取我的版本信息失败,GetValue 现在不再找到 属性。
private int GetMessageVersion(JObject jObject)
{
JToken versionToken = jObject.GetValue(nameof(MessageEnvelop.Version));
if (versionToken is null)
{
throw new MandatoryPropertyMissingException(nameof(MessageEnvelop.Version));
}
return versionToken.Value<int>();
}
我使用 JObject.Load
和来自转换器的 reader 来创建使用的 JObject,所以我假设这将确保这个命名约定的翻译......错误的假设。
如何让我的转换器使用正确的命名策略,从而更有弹性?
JObject.GetValue(String)
is case sensitive, so if you just want to mimic the serializer's ordinal case insensitivity, you can call JObject.GetValue(String, StringComparison)
:
JToken versionToken = jObject.GetValue(nameof(MessageEnvelop.Version), StringComparison.OrdinalIgnoreCase);
这应该足以处理由于骆驼大小写引起的命名差异,但如果您想处理更一般的命名更改,您可以使用 Json.NET 自己的 contract information 来确定 JSON 属性 来自基础 .NET 属性 名称的名称,使用以下扩展方法:
public static partial class JsonExtensions
{
public static T GetValueByUnderlyingName<T>(this JObject jObject, IContractResolver resolver, Type type, string underlyingName)
{
if (!resolver.TryGetJsonPropertyNameByUnderlyingName(type, underlyingName, out var jsonName))
throw new ArgumentException(underlyingName);
JToken versionToken = jObject.GetValue(jsonName);
if (versionToken is null)
throw new MandatoryPropertyMissingException(underlyingName);
return versionToken.Value<T>();
}
public static bool TryGetJsonPropertyByUnderlyingName(this IContractResolver resolver, Type type, string underlyingName, out JsonProperty property) =>
TryGetJsonPropertyByUnderlyingName(resolver, type, underlyingName, false, out property);
public static bool TryGetJsonPropertyByUnderlyingName(this IContractResolver resolver, Type type, string underlyingName, bool exact, out JsonProperty property)
{
resolver = resolver ?? throw new ArgumentNullException(nameof(resolver));
var contract = resolver.ResolveContract(type) as JsonObjectContract;
if (contract == null)
throw new ArgumentException();
property = null;
foreach (var p in contract.Properties)
{
if (p.UnderlyingName == underlyingName)
{
property = p;
return true;
}
if (property == null && string.Equals(p.UnderlyingName, underlyingName, StringComparison.OrdinalIgnoreCase))
{
property = p;
}
}
return property != null;
}
public static bool TryGetJsonPropertyNameByUnderlyingName(this IContractResolver resolver, Type type, string underlyingName, out string jsonName) =>
TryGetJsonPropertyNameByUnderlyingName(resolver, type, underlyingName, false, out jsonName);
public static bool TryGetJsonPropertyNameByUnderlyingName(this IContractResolver resolver, Type type, string underlyingName, bool exact, out string jsonName)
{
if (resolver.TryGetJsonPropertyByUnderlyingName(type, underlyingName, exact, out var p))
{
jsonName = p.PropertyName;
return true;
}
jsonName = null;
return false;
}
}
要使用它,请在 JsonConverter<T>.ReadJson()
中为 resolver
参数传入 serializer.ContractResolver
,并为 type
参数传入 objectType
,如下所示:
var version = jObject.GetValueByUnderlyingName<int>(serializer.ContractResolver, objectType, nameof(MessageEnvelop.Version));
演示 fiddle here.
我为 NewtonSoft 制作了一个自定义的 JsonConvert JSON...经过全面测试,现在可以正常工作一段时间了。但是有人说属性需要驼峰式而不是 PascalCased。
所以,好的...我更改了默认设置中的 NamingStrategy。
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
DateFormatHandling = DateFormatHandling.IsoDateFormat,
DateTimeZoneHandling = DateTimeZoneHandling.Utc,
Converters = new List<JsonConverter>
{
new StringEnumConverter(),
new MessageEnvelopJsonConverter(PayloadTypes.GetMappings())
},
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
}
};
果然,输出工作正常......这并不奇怪;转换器仅使用读取来确保接口的正确实现属性,但没有写入实现。
现在获取我的版本信息失败,GetValue 现在不再找到 属性。
private int GetMessageVersion(JObject jObject)
{
JToken versionToken = jObject.GetValue(nameof(MessageEnvelop.Version));
if (versionToken is null)
{
throw new MandatoryPropertyMissingException(nameof(MessageEnvelop.Version));
}
return versionToken.Value<int>();
}
我使用 JObject.Load
和来自转换器的 reader 来创建使用的 JObject,所以我假设这将确保这个命名约定的翻译......错误的假设。
如何让我的转换器使用正确的命名策略,从而更有弹性?
JObject.GetValue(String)
is case sensitive, so if you just want to mimic the serializer's ordinal case insensitivity, you can call JObject.GetValue(String, StringComparison)
:
JToken versionToken = jObject.GetValue(nameof(MessageEnvelop.Version), StringComparison.OrdinalIgnoreCase);
这应该足以处理由于骆驼大小写引起的命名差异,但如果您想处理更一般的命名更改,您可以使用 Json.NET 自己的 contract information 来确定 JSON 属性 来自基础 .NET 属性 名称的名称,使用以下扩展方法:
public static partial class JsonExtensions
{
public static T GetValueByUnderlyingName<T>(this JObject jObject, IContractResolver resolver, Type type, string underlyingName)
{
if (!resolver.TryGetJsonPropertyNameByUnderlyingName(type, underlyingName, out var jsonName))
throw new ArgumentException(underlyingName);
JToken versionToken = jObject.GetValue(jsonName);
if (versionToken is null)
throw new MandatoryPropertyMissingException(underlyingName);
return versionToken.Value<T>();
}
public static bool TryGetJsonPropertyByUnderlyingName(this IContractResolver resolver, Type type, string underlyingName, out JsonProperty property) =>
TryGetJsonPropertyByUnderlyingName(resolver, type, underlyingName, false, out property);
public static bool TryGetJsonPropertyByUnderlyingName(this IContractResolver resolver, Type type, string underlyingName, bool exact, out JsonProperty property)
{
resolver = resolver ?? throw new ArgumentNullException(nameof(resolver));
var contract = resolver.ResolveContract(type) as JsonObjectContract;
if (contract == null)
throw new ArgumentException();
property = null;
foreach (var p in contract.Properties)
{
if (p.UnderlyingName == underlyingName)
{
property = p;
return true;
}
if (property == null && string.Equals(p.UnderlyingName, underlyingName, StringComparison.OrdinalIgnoreCase))
{
property = p;
}
}
return property != null;
}
public static bool TryGetJsonPropertyNameByUnderlyingName(this IContractResolver resolver, Type type, string underlyingName, out string jsonName) =>
TryGetJsonPropertyNameByUnderlyingName(resolver, type, underlyingName, false, out jsonName);
public static bool TryGetJsonPropertyNameByUnderlyingName(this IContractResolver resolver, Type type, string underlyingName, bool exact, out string jsonName)
{
if (resolver.TryGetJsonPropertyByUnderlyingName(type, underlyingName, exact, out var p))
{
jsonName = p.PropertyName;
return true;
}
jsonName = null;
return false;
}
}
要使用它,请在 JsonConverter<T>.ReadJson()
中为 resolver
参数传入 serializer.ContractResolver
,并为 type
参数传入 objectType
,如下所示:
var version = jObject.GetValueByUnderlyingName<int>(serializer.ContractResolver, objectType, nameof(MessageEnvelop.Version));
演示 fiddle here.