Json.Net 用到後面,我們免不了有時會要 Custom Converter 去做序列化的客製動作。

使用 Json.Net 要做自定義序列化,我們可以 Custom Json.Net 的 Converter,為此需要建立一個 Converter 類別,將之繼承自 JsonConverter,接著將繼承而來的方法實作即可。

CanConverter 用來決定該型別是否可被該 Converter 處理、WriteJson 用來序列化物件、ReadJson 用來解序列化物件。

像是我們可以像下面這樣做個簡易的 Converter,裡面只是單純的將物件序列化與解序列化。

using System;
using Newtonsoft.Json;

namespace LevelUp.Converter
{
    public class ConcreteTypeConverter<TConcrete> : JsonConverter
    {
        public override bool CanConvert( Type objectType)
        {
            return true ;
        }

        public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            return serializer.Deserialize<TConcrete>(reader);
        }

        public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer)
        {
            serializer.Serialize(writer, value);
        }
    }
}

當我們在序列化物件時,若物件的成員屬性是 Interface,就可以用來指定序列化與解序列化時實際所要用的型態。

[JsonConverter(typeof(ConcreteTypeConverter<DecisionNode[]>))]
IDecisionNode[] Nodes { get ; }

或者是像下面這段用來處理 Dictionary 的序列話與解序列化的 Converter 程式,裡面會用 JsonWriter 去處理序列化的動作、用 JsonReader 去處理解序列化的動作,寫起來比較複雜些,但可進行比較進階的處理。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json;

namespace LevelUp.Converter
{
    public class DictionaryConverter<TKey, TValue> : JsonConverter
    {
        #region  Methods
        private static bool TypeImplementsGenericInterface( Type concreteType, Type interfaceType)
        {
            return concreteType.GetInterfaces()
                   .Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == interfaceType);
        }
        public override bool CanConvert( Type objectType)
        {
            return (typeof (IDictionary).IsAssignableFrom(objectType) ||
                    TypeImplementsGenericInterface(objectType, typeof(IDictionary <,>)));
        }

        public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            if (reader.TokenType == JsonToken.None) return null ;

            var dict = new Dictionary<TKey, TValue>();

            reader.Read();

            while (reader.TokenType == JsonToken.StartArray)
            {
                reader.Read();

                var key = serializer.Deserialize<TKey>(reader);

                reader.Read();
                var value = serializer.Deserialize<TValue>(reader);

                reader.Read();
                reader.Read();

                dict.Add(key, value);
            }

            return dict;
        }

        public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer)
        {
            var dict = value as IDictionary;
            var keys = dict.Keys;
            var values = dict.Values;
            var valueEnumerator = values.GetEnumerator();

            writer.WriteStartArray();
            foreach (var key in keys)
            {
                valueEnumerator.MoveNext();

                writer.WriteStartArray();
                serializer.Serialize(writer, key);
                serializer.Serialize(writer, valueEnumerator.Current);
                writer.WriteEndArray();
            }
            writer.WriteEndArray();
        }
        #endregion  Methods
    }
}