创建一个自定义JsonConverter来处理System.Text.Encoding对象

本文关键字:处理 System Text 对象 Encoding JsonConverter 自定义 一个 创建 | 更新日期: 2023-09-27 18:04:32

我已经编写了一个自定义的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不是值类型或非抽象类。

我错过了什么吗?

创建一个自定义JsonConverter来处理System.Text.Encoding对象

我看到你的转换器有三个问题。

  1. 您在CanConvert()中使用了错误的检查。
  2. 您在序列化时使用了错误的Encoding名称。
  3. 在反序列化时,您使用了错误的方法从读取器获取值。

让我们一个一个来。

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

第二,当在WriteJson中序列化Encoding时,您输出的是EncodingName属性,这是编码的人类可读的显示名称,而不是代码页名称。如果您查看Encoding.GetEncoding(string)方法的文档,它说:

参数

<

名称/em>

类型:系统。字符串

首选编码的代码页名称。WebName属性返回的任何值都是有效的。可能的值列在出现在Encoding类主题中的表的Name列中。

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

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

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

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

尝试:

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