c# - Newtonsoft.JSON 的 ContractResolver 与自定义转换器冲突
问题描述
我有这个示例分层数据:
{
"Name": "Car 1",
"AvailableColors": [
"Red",
"Green"
],
"RelatedItems": {
"Brand": {
"Name": "Brand 1",
"RelatedItems": {
"ImportingCompanies": [
{
"Name": "Company 1",
"RelatedItems": {
"CeoName": "CEO 1"
}
},
{
"Name": "Company 2",
"RelatedItems": {
"CeoName": "CEO 2"
}
}
]
}
}
}
}
基本上我需要的是将任何属性的所有属性复制RelatedItems
到其父对象。结果应该变成:
{
"Name": "Car 1",
"AvailableColors": [
"Red",
"Green"
],
"Brand": {
"Name": "Brand 1",
"ImportingCompanies": [
{
"Name": "Company 1",
"CeoName": "CEO 1"
},
{
"Name": "Company 2",
"CeoName": "CEO 2"
}
]
}
}
我正在使用 ASP.NET Core,并以这种方式配置了我的启动:
.AddNewtonsoftJson(options =>
{
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
options.SerializerSettings.Converters.Add(new RelatedItemsFlattenerJsonConverter());
});
这是我写的代码RelatedItemsFlattenerJsonConverter
:
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var stopwatch = new Stopwatch();
stopwatch.Start();
JToken token = JToken.FromObject(value);
if (token.Type == JTokenType.Array)
{
var array = (JArray)token;
foreach (var item in token)
{
FlattenRelatedItems(item);
}
}
else if (token.Type == JTokenType.Object)
{
var @object = (JObject)token;
FlattenRelatedItems(@object);
}
token.WriteTo(writer);
stopwatch.Stop();
}
private void FlattenRelatedItems(JToken token)
{
if (token.Type == JTokenType.Array)
{
var array = (JArray)token;
foreach (var item in token)
{
FlattenRelatedItems(item);
}
}
else if (token.Type == JTokenType.Object)
{
var @object = (JObject)token;
var relatedItemsProperty = @object.Properties().FirstOrDefault(i => i.Name.ToLower() == "RelatedItems".ToLower());
if (relatedItemsProperty.IsNotNull())
{
var relatedItems = @object[relatedItemsProperty.Name];
@object.Remove(relatedItemsProperty.Name);
var keys = ((JObject)relatedItems).Properties().Select(i => i.Name).ToList();
foreach (var key in keys)
{
@object.Add(key, relatedItems[key]);
}
}
var properties = @object.Properties().ToList();
foreach (var property in properties)
{
FlattenRelatedItems(@object[property.Name]);
}
}
}
它就像一个魅力。但是,当我添加 custom 时RelatedItemsFlattenerJsonConverter
,我的 API 响应的大小写将变为PascalCase d。当我不添加它时,它ContractResolver
受到尊重,我的 API 响应是camelCase d。我应该怎么做才能同时拥有自定义转换器和API 的camelCase ?
解决方案
当您调用JToken.FromObject(value)
insideWriteJson
时,您没有将序列化程序传递给它,因此它不知道CamelCasePropertyNamesContractResolver
您已配置的(或任何其他设置)。 JToken.FromObject()
默认情况下在内部使用新的序列化程序实例。
但是,在这种情况下,如果按原样传入序列化程序,则可能会遇到自引用循环,因为转换器会尝试调用自身。您需要做的是创建自己的JsonSerializer
inside实例,WriteJson
然后从传递给WriteJson
. 然后将该新的序列化程序传递给JToken.FromObject()
.
换句话说,改变这一行:
JToken token = JToken.FromObject(value);
对此:
var innerSerializer = new JsonSerializer();
innerSerializer.ContractResolver = serializer.ContractResolver;
// if any other settings from the outer serializer are needed, copy them here
JToken token = JToken.FromObject(value, innerSerializer);
推荐阅读
- css - 使用 webpack 保持文件/文件夹结构将 SCSS 最小化为 CSS
- docker - docker stdin 和 tty 标志如何在容器的幕后工作?
- java - 绘制带和不带着色器的三角形 VBO
- c++ - Validating an NMEA sentence using C++
- nginx - 在 VPS 上以生产模式在 Ubuntu 16.04 LTS 上的 NginX/Passenger 服务器上部署 Flask 应用程序
- python - 在 QTextDocument 中单击链接时生成自定义事件
- jmeter - 我想在循环控制器中使用所有提取的值
- ruby-on-rails - 您正在从