创建自定义JsonConverter以处理System.Text.Encoding对象

时间:2015-07-25 05:54:23

标签: c# .net json json.net

我编写了一个自定义的JsonConverter,我希望能够在我的类中序列化和反序列化Encoding个对象:

public class EncodingConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType.IsSubclassOf(typeof(Encoding));
    }

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

    public override bool CanRead { get { return true; } }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var name = reader.ReadAsString();
        return Encoding.GetEncoding(name);
    }
}

但是,当我运行以下测试代码时,调用DeserializeObject时会出现异常,并且永远不会调用ReadJson方法。

class Program
{
    private static void Main(string[] args)
    {
        var test = new TestClass();

        var jsonSettings = new JsonSerializerSettings
        {
            Converters = new[] { new EncodingConverter(), }
        };

        var json = JsonConvert.SerializeObject(test, jsonSettings);

        var test2 = JsonConvert.DeserializeObject<TestClass>(json, jsonSettings);
    }
}

class TestClass
{
    public string Property1;
    public Encoding Encoding = Encoding.UTF8;
}

异常消息是:

  

目标类型System.Text.Encoding不是值类型或非抽象类。

我错过了什么吗?

2 个答案:

答案 0 :(得分:1)

我看到您的转换器存在三个问题。

  1. 您在CanConvert()使用了错误的支票。
  2. 序列化时,Encoding使用了错误的名称。
  3. 在反序列化时,您使用了错误的方法从读取器获取值。
  4. 让我们一次拍摄这些。

    首先,在CanConvert方法中,您使用objectType.IsSubclassOf(typeof(Encoding))来确定转换器是否应该处理Encoding。这在序列化上工作正常,因为你有一个具体的编码实例(例如UTF8Encoding),它确实是Encoding的子类。但是,在反序列化时,反序列化器不知道您要进行的具体编码类型,因此传递给转换器的类型只是Encoding。由于Encoding不是其自身的子类,CanConvert返回false,并且您的ReadJson方法永远不会被调用。这使得Json.Net尝试实例化Encoding本身,它无法做到(因为Encoding是抽象的),所以它会抛出你在问题中提到的错误。您应该在typeof(Encoding).IsAssignableFrom(objectType)方法中使用CanConvert

    其次,在序列化Encoding WriteJson时,您正在输出EncodingName属性,该属性是编码的人类可读显示名称,而不是代码页名称。如果您查看Encoding.GetEncoding(string)方法的文档,则会说:

      

    <强>参数

         

    名称

         

    输入:System.String

         

    首选编码的代码页名称。 WebName属性返回的任何值都是有效的。可能的值列在“编码”类主题中显示的表的“名称”列中。

    因此,如果您希望能够使用此值稍后重新构建WebName WriteJson,则应在Encoding方法中输出ReadJson属性的值}}

    第三,在ReadJson方法中,您使用reader.ReadAsString()尝试从JSON获取编码名称。这不会像你期望的那样工作。当Json.Net调用ReadJson时,阅读器已经定位在当前值。当您致电ReadAsString()时,会将阅读器推进到 next 令牌,然后尝试将该令牌解释为字符串。您真正想要做的只是获取当前令牌的值,您可以使用Value属性来执行此操作。由于Value的类型为object,因此您需要将其强制转换为字符串。

    以下是转换器的更正代码:

    public class EncodingConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return typeof(Encoding).IsAssignableFrom(objectType);
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            writer.WriteValue(((Encoding)value).WebName);
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            return Encoding.GetEncoding((string)reader.Value);
        }
    }
    

    小提琴:https://dotnetfiddle.net/UmLynX

答案 1 :(得分:-1)

尝试:

    public class CustomConverter : JsonConverter
    {

        public override bool CanConvert(System.Type objectType)
        {
            return true;// objectType.IsAssignableFrom(typeof(Encoding));
        }

        public override object ReadJson(JsonReader reader, System.Type objectType, object existingValue, JsonSerializer serializer)
        {
            return Encoding.GetEncoding(Convert.ToString(reader.Value));
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var t = (Test)value;
            var e = (Encoding)t.MyProperty;
            writer.WriteValue(e.BodyName);
            //serializer.Serialize(writer, e.BodyName);
        }
    }

Main

            var o = new Test { MyProperty = Encoding.UTF8 };
            var s = new JsonSerializerSettings
            {
                Converters = new[] { new CustomConverter() }
            };
            var v = JsonConvert.SerializeObject(o, s);
            var o2 = new Test();
            o2.MyProperty = Encoding.GetEncoding(JsonConvert.DeserializeObject(v, s).ToString());