我正在编写JsonConverter来执行一些我需要在读/写时完成的转换任务。特别是,我要采用现有的序列化行为,并在写入时添加一些其他属性/在读取时读取这些其他属性。

JsonConverter内部,我想利用传递的JsonSerializer实例执行大多数转换功能。但是,当我这样做时,我最终陷入了递归循环,在此循环中,序列化程序调用了我的转换器,后者又调用了序列化程序,后者又调用了转换器等。

我见过人们做诸如JsonConvert.SerializeObject之类的事情,除了this之外,从序列化程序实例中传入所有转换器。但是,这对我不起作用,因为它绕过了我在序列化程序上所做的所有其他自定义,例如自定义契约(Contract)解析器和DateTime处理。

有没有办法我可以:

  • 使用传递给我的序列化程序实例,但以某种方式排除我的转换器或
  • 复制传递给我的序列化器(无需手动构造一个新的序列化器并按属性复制它的属性)并删除我的转换器?
  • 最佳答案

    这是一个非常普遍的问题。使用“JsonConvert.SerializeObject”不是一个坏主意。但是,在某些情况下(通常是集合)可以使用的一个技巧是在写入时将其强制转换为接口(interface),在读取时将其反序列化为简单的派生。

    下面是一个简单的转换器,它处理可能被序列化为一组KVP而不是看起来像对象的字典(在这里显示我的年龄:))

    注意“WriteJson”强制转换为IDictionary ,“ReadJson”使用“DummyDictionary”。您最终得到了正确的结果,但是使用了传递的序列化器而不会导致递归。

    /// <summary>
    /// Converts a <see cref="KeyValuePair{TKey,TValue}"/> to and from JSON.
    /// </summary>
    public class DictionaryAsKVPConverter<TKey, TValue> : JsonConverter
    {
        /// <summary>
        /// Determines whether this instance can convert the specified object type.
        /// </summary>
        /// <param name="objectType">Type of the object.</param>
        /// <returns>
        ///     <c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
        /// </returns>
        public override bool CanConvert(Type objectType)
        {
            if (!objectType.IsValueType && objectType.IsGenericType)
                return (objectType.GetGenericTypeDefinition() == typeof(Dictionary<,>));
    
            return false;
        }
    
        /// <summary>
        /// Writes the JSON representation of the object.
        /// </summary>
        /// <param name="writer">The <see cref="JsonWriter"/> to write to.</param>
        /// <param name="value">The value.</param>
        /// <param name="serializer">The calling serializer.</param>
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var dictionary = value as IDictionary<TKey, TValue>;
            serializer.Serialize(writer, dictionary);
        }
    
        /// <summary>
        /// Reads the JSON representation of the object.
        /// </summary>
        /// <param name="reader">The <see cref="JsonReader"/> to read from.</param>
        /// <param name="objectType">Type of the object.</param>
        /// <param name="existingValue">The existing value of object being read.</param>
        /// <param name="serializer">The calling serializer.</param>
        /// <returns>The object value.</returns>
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            Dictionary<TKey, TValue> dictionary;
    
            if (reader.TokenType == JsonToken.StartArray)
            {
                dictionary = new Dictionary<TKey, TValue>();
                reader.Read();
                while (reader.TokenType == JsonToken.StartObject)
                {
                    var kvp = serializer.Deserialize<KeyValuePair<TKey, TValue>>(reader);
                    dictionary[kvp.Key] = kvp.Value;
                    reader.Read();
                }
            }
            else if (reader.TokenType == JsonToken.StartObject)
                // Use DummyDictionary to fool JsonSerializer into not using this converter recursively
                dictionary = serializer.Deserialize<DummyDictionary>(reader);
            else
                dictionary = new Dictionary<TKey, TValue>();
    
            return dictionary;
        }
    
        /// <summary>
        /// Dummy to fool JsonSerializer into not using this converter recursively
        /// </summary>
        private class DummyDictionary : Dictionary<TKey, TValue> { }
    }
    

    09-11 19:52