我编写了一个自定义的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不是值类型或非抽象类。
我错过了什么吗?
答案 0 :(得分:1)
我看到您的转换器存在三个问题。
CanConvert()
使用了错误的支票。Encoding
使用了错误的名称。让我们一次拍摄这些。
首先,在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);
}
}
答案 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());