序列化Windows-1252编码时遇Newtonsoft.Json序列化异常求助
问题描述
尝试将System.Text.Encoding.CodePages包中的Windows-1252编码序列化为JSON时,抛出异常:
Newtonsoft.Json.JsonSerializationException: Error getting value from 'WindowsCodePage' on 'System.Text.SBCSCodePageEncoding'
但UTF8等内置编码可以正常完成序列化操作,复现代码如下:
using System; using System.Text; using Newtonsoft.Json; public class Program { public static void Main() { Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); var workingEncoding = Encoding.UTF8; var notWorkingEncoding = Encoding.GetEncoding("windows-1252"); Console.WriteLine(workingEncoding.EncodingName); Console.WriteLine(notWorkingEncoding.EncodingName); Console.WriteLine(); var workingSerialized = JsonConvert.SerializeObject(workingEncoding); Console.WriteLine(workingSerialized); Console.WriteLine(); var notWorkingSerialized = JsonConvert.SerializeObject(notWorkingEncoding); Console.WriteLine(notWorkingSerialized); } }
问题原因
通过CodePagesEncodingProvider获取的Windows-1252编码属于SBCSCodePageEncoding类型,该类型的WindowsCodePage是内部属性。Newtonsoft.Json默认序列化时会通过反射遍历所有属性,尝试访问这个内部属性时触发权限异常。而UTF8这类内置编码属于UTF8Encoding,不存在此类内部属性的访问问题。
解决方法
方法1:自定义JsonConverter序列化关键信息
创建自定义转换器,只序列化编码的核心标识信息,避免访问内部属性,同时支持反序列化:
using Newtonsoft.Json; using System.Text; public class EncodingConverter : JsonConverter<Encoding> { public override void WriteJson(JsonWriter writer, Encoding value, JsonSerializer serializer) { writer.WriteStartObject(); writer.WritePropertyName("CodePage"); writer.WriteValue(value.CodePage); writer.WritePropertyName("EncodingName"); writer.WriteValue(value.EncodingName); writer.WritePropertyName("WebName"); writer.WriteValue(value.WebName); writer.WriteEndObject(); } public override Encoding ReadJson(JsonReader reader, Type objectType, Encoding existingValue, bool hasExistingValue, JsonSerializer serializer) { var jo = Newtonsoft.Json.Linq.JObject.Load(reader); var codePage = (int)jo["CodePage"]; return Encoding.GetEncoding(codePage); } }
使用示例:
var settings = new JsonSerializerSettings(); settings.Converters.Add(new EncodingConverter()); var serialized = JsonConvert.SerializeObject(notWorkingEncoding, settings); Console.WriteLine(serialized); // 反序列化示例 var deserializedEncoding = JsonConvert.DeserializeObject<Encoding>(serialized, settings);
方法2:直接序列化编码标识(最简方案)
如果只需要保存编码的标识信息,无需序列化整个对象,可以直接序列化WebName或CodePage,反序列化时重新获取编码:
// 序列化 var serialized = JsonConvert.SerializeObject(notWorkingEncoding.WebName); // 反序列化 var webName = JsonConvert.DeserializeObject<string>(serialized); var encoding = Encoding.GetEncoding(webName);
方法3:忽略序列化错误(不推荐)
通过配置序列化设置忽略无法访问的成员和错误,这种方式可能丢失属性信息,仅作为临时替代方案:
var settings = new JsonSerializerSettings { Error = (sender, args) => args.ErrorContext.Handled = true, ContractResolver = new Newtonsoft.Json.Serialization.DefaultContractResolver { IgnoreSerializableInterface = true, IgnoreSerializableAttribute = true } }; var serialized = JsonConvert.SerializeObject(notWorkingEncoding, settings);
内容的提问来源于stack exchange,提问作者PJDev
相关产品推荐
相关产品推荐

