制作自定义JsonConverter与JSON.NET序列化时尊重ItemTypeNameHandling [英] Making custom JsonConverter respect ItemTypeNameHandling when serializing with JSON.NET

查看:471
本文介绍了制作自定义JsonConverter与JSON.NET序列化时尊重ItemTypeNameHandling的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个对象与基类的子对象的列表。子对象需要自定义转换器。我不能让我的自定义转换器方面 ItemTypeNameHandling 选项。

I have an object with a list of base class sub-objects. Sub-objects need a custom converter. I can't make my custom converter respect ItemTypeNameHandling option.

样品code(创建一个新的C#控制台项目,添加JSON.NET的NuGet包):

Sample code (create a new C# Console project, add JSON.NET NuGet package):

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);
        }
    }
}

生成的输出:

Produced output:

{
  "toys": [
    {},
    {}
  ]
}

必要的输出(这是,如果我评会发生什么 ItemConverterType = typeof运算(ToyConverter),行):

{
  "toys": [
    {
      "$type": "My.Spintop, Serialization"
    },
    {
      "$type": "My.Ball, Serialization"
    }
  ]
}

我试着暂时改变 ToyConverter.WriteJson 方法 serializer.TypeNameHandling 的价值,但它影响无关属性。 (当然,我真正的转换器是比这更复杂,这只是与基本功能的一个例子。)

I've tried temporarily changing value of serializer.TypeNameHandling in ToyConverter.WriteJson method, but it affects unrelated properties. (Of course, my real converter is more complex than that. It's just an example with base functionality.)

问:如何使我的自定义 JsonConverter 关于 ItemTypeNameHandling 属性 JsonProperty 属性?

Question: How to make my custom JsonConverter respect ItemTypeNameHandling property of JsonProperty attribute?

推荐答案

已经钻研源$ C ​​$下Json.Net(4.5版发布11),它看起来好像你想要做什么是不可能的。

Having delved into the source code for Json.Net (version 4.5 release 11), it looks as though what you want to do is not possible.

的关键在于写入到输出得到类型是这样的方式:

The key to getting types written to the output is this method:

Newtonsoft.Json.Serialization.JsonSerializerInternalWriter
    .ShouldWriteType(TypeNameHandling typeNameHandlingFlag, JsonContract contract,
        JsonProperty member, JsonContainerContract containerContract,
        JsonProperty containerProperty)

这是 containerContract containerProperty 参数是很重要的位置。当序列化的没有的转换器,这些参数提供, ShouldWriteType 能够利用它们来找出 TypeNameHandling 来使用。

It's the containerContract and containerProperty parameters that are important here. When serializing without a converter, these parameters are supplied and ShouldWriteType is able to use them to figure out what TypeNameHandling to use.

在序列化的的转换器,但是,这两个参数都没有提供。这似乎是因为 ToyConverter.WriteJson 将导致在这样的呼吁 Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue

When serializing with a converter, however, those two parameters are not supplied. This appears to be because ToyConverter.WriteJson results in a call to Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue like this:

SerializeValue(jsonWriter, value, GetContractSafe(value), null, null, null);

请注意,最后两个参数是实际上是一个 JsonContainerContract containerContract JsonProperty containerProperty ,并流传下来的链到 ShouldWriteType 方法。这就存在一个问题:因为他们都为空,逻辑的 ShouldWriteType 方法意味着它返回,因此,型不被写入。

Note that the last two parameters are in fact a JsonContainerContract containerContract and JsonProperty containerProperty, and are passed down the chain to the ShouldWriteType method. Therein lies the problem: because they are null, the logic of the ShouldWriteType method means that it returns false, thus the type is not written.

编辑:

通过这个,则可以解决这一通过自定义问题 WriteJson 的转换器的方法:

Inspired by this, you could workaround this problem by customising the WriteJson method of your converter:

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的来源

Note that that RemoveAssemblyDetails method is ripped straight from the Json.Net source.

您当然会需要修改 WriteJson 方法输出领域的休息,但希望这是卓有成效的。

You will of course need to modify the WriteJson method to output the rest of the fields, but hopefully that does the trick.

这篇关于制作自定义JsonConverter与JSON.NET序列化时尊重ItemTypeNameHandling的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆