我有一个带有基类子对象列表的对象。子对象需要自定义转换器。我无法让我的自定义转换器尊重 ItemTypeNameHandling
选项。
示例代码(创建一个新的C#控制台项目,添加JSON.NET NuGet包):
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace My {
class Program {
private static void Main () {
Console.WriteLine(JsonConvert.SerializeObject(
new Box { toys = { new Spintop(), new Ball() } },
Formatting.Indented));
Console.ReadKey();
}
}
[JsonObject] class Box
{
[JsonProperty (
ItemConverterType = typeof(ToyConverter),
ItemTypeNameHandling = TypeNameHandling.Auto)]
public List<Toy> toys = new List<Toy>();
}
[JsonObject] class Toy {}
[JsonObject] class Spintop : Toy {}
[JsonObject] class Ball : Toy {}
class ToyConverter : JsonConverter {
public override void WriteJson (JsonWriter writer, object value, JsonSerializer serializer) {
serializer.Serialize(writer, value);
}
public override object ReadJson (JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
return serializer.Deserialize(reader, objectType);
}
public override bool CanConvert (Type objectType) {
return typeof(Toy).IsAssignableFrom(objectType);
}
}
}
产生的输出:
{
"toys": [
{},
{}
]
}
必要的输出(如果我注释 ItemConverterType = typeof(ToyConverter),
行,就会发生这种情况):
{
"toys": [
{
"$type": "My.Spintop, Serialization"
},
{
"$type": "My.Ball, Serialization"
}
]
}
我尝试临时更改 ToyConverter.WriteJson
方法中 serializer.TypeNameHandling
的值,但它会影响不相关的属性。 (当然,我真正的转换器比这更复杂。这只是一个具有基本功能的示例。)
问题:如何使我的自定义 JsonConverter
尊重 JsonProperty
属性的 ItemTypeNameHandling
属性?
最佳答案
深入研究 Json.Net(版本 4.5 版本 11)的源代码后,看起来您想要做的事情似乎是不可能的。
将类型写入输出的关键是这个方法:
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter
.ShouldWriteType(TypeNameHandling typeNameHandlingFlag, JsonContract contract,
JsonProperty member, JsonContainerContract containerContract,
JsonProperty containerProperty)
这里重要的是 containerContract
和 containerProperty
参数。在没有转换器的情况下进行序列化时,会提供这些参数,并且 ShouldWriteType
能够使用它们来确定要使用的 TypeNameHandling
。
但是,当使用转换器进行序列化时,不会提供这两个参数。这似乎是因为 ToyConverter.WriteJson
导致对 Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue
的调用,如下所示:
SerializeValue(jsonWriter, value, GetContractSafe(value), null, null, null);
请注意,最后两个参数实际上是 JsonContainerContract containerContract
和 JsonProperty containerProperty
,并沿链向下传递到 ShouldWriteType
方法。问题就在这里:因为它们是 null,所以 ShouldWriteType
方法的逻辑意味着它返回 false
,从而未写入类型。
编辑:
灵感来自this ,您可以通过自定义转换器的 WriteJson
方法来解决此问题:
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteStartObject();
writer.WritePropertyName("$type");
writer.WriteValue(RemoveAssemblyDetails(value.GetType().AssemblyQualifiedName.ToString()));
writer.WriteEndObject();
}
private static string RemoveAssemblyDetails(string fullyQualifiedTypeName)
{
StringBuilder builder = new StringBuilder();
// loop through the type name and filter out qualified assembly details from nested type names
bool writingAssemblyName = false;
bool skippingAssemblyDetails = false;
for (int i = 0; i < fullyQualifiedTypeName.Length; i++)
{
char current = fullyQualifiedTypeName[i];
switch (current)
{
case '[':
writingAssemblyName = false;
skippingAssemblyDetails = false;
builder.Append(current);
break;
case ']':
writingAssemblyName = false;
skippingAssemblyDetails = false;
builder.Append(current);
break;
case ',':
if (!writingAssemblyName)
{
writingAssemblyName = true;
builder.Append(current);
}
else
{
skippingAssemblyDetails = true;
}
break;
default:
if (!skippingAssemblyDetails)
builder.Append(current);
break;
}
}
return builder.ToString();
}
请注意,RemoveAssemblyDetails
方法是直接从 Json.Net source 中提取的。 .
您当然需要修改 WriteJson
方法来输出其余字段,但希望这能解决问题。
关于c# - 使用 JSON.NET 进行序列化时,使自定义 JsonConverter 尊重 ItemTypeNameHandling,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14182961/