【问题标题】:Creating a custom JsonConverter to handle System.Text.Encoding objects创建自定义 JsonConverter 来处理 System.Text.Encoding 对象
【发布时间】:2015-07-25 05:54:23
【问题描述】:

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

我错过了什么吗?

【问题讨论】:

  • objectType.IsAssignableFrom(typeof(Encoding)) 计算结果为假。
  • 您的TestClass 不能从Encoding 分配。因此(Encoding)value 无效。
  • @Amit Kumar Ghosh,此转换器仅用于编码对象

标签: c# .net json json.net


【解决方案1】:

我发现您的转换器存在三个问题。

  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) 方法的文档,它会说:

参数

名字

类型:System.String

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

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

第三,在您的 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 是我需要的……其他 2 个是小问题。
【解决方案2】:

试试:

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

【讨论】:

  • 我的目标是为某种类型(编码)编写一个自定义转换器,而不是编写一个完全手动的全对象序列化器。我正在使用的真实类型比示例 TestClass 复杂得多。
猜你喜欢
  • 1970-01-01
  • 2015-06-24
  • 2021-12-13
  • 2022-06-30
  • 2023-02-02
  • 2018-04-19
  • 2011-08-24
  • 1970-01-01
  • 2011-01-15
相关资源
最近更新 更多