求助:使用JsonConverterAttribute序列化Encoding属性时抛出异常
尝试序列化Encoding类型的属性,编写了对应的转换器JsonEncodingConverter。通过JsonSerializerOptions添加转换器时一切正常,但用JsonConverterAttribute标记属性时,序列化和反序列化均抛出异常。
示例代码
using System.Text; using System.Text.Json; using System.Text.Json.Serialization; namespace TestJsonConverterBug { internal class Program { static readonly JsonSerializerOptions _options = new(); static void Main() { _options.Converters.Add(new JsonEncodingConverter()); TestOptions(); TestAttributeSerialize(); TestAttributeDeserialize(); } // 使用Options正常工作 static void TestOptions() { var obj = new SerializableClass1(); var json = JsonSerializer.Serialize(obj, _options); var enc = JsonSerializer.Deserialize<SerializableClass1>(json, _options); } // 使用Converter Attribute无法工作 static void TestAttributeSerialize() { var obj = new SerializableClass2(); var json = JsonSerializer.Serialize(obj); } // 使用Converter Attribute无法工作 static void TestAttributeDeserialize() { var obj = new SerializableClass2(); var json = JsonSerializer.Serialize(obj, _options); var enc = JsonSerializer.Deserialize<SerializableClass2>(json); } } internal class SerializableClass1 { public SerializableClass1() { Encoding = Encoding.ASCII; } public Encoding Encoding { get; set; } } internal class SerializableClass2 { public SerializableClass2() { Encoding = Encoding.ASCII; } [JsonConverter(typeof(JsonEncodingConverter))] public Encoding Encoding { get; set; } } internal class JsonEncodingConverter : JsonConverter<Encoding> { public override bool CanConvert(Type typeToConvert) => typeToConvert.IsAssignableTo(typeof(Encoding)); public override Encoding? Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options ) => Encoding.GetEncoding(reader.GetString()!); public override void Write( Utf8JsonWriter writer, Encoding value, JsonSerializerOptions options ) => writer.WriteStringValue(value.EncodingName); } }
异常信息
TestAttributeSerialize异常
System.InvalidOperationException HResult=0x80131509 Message=类型'System.Text.Encoding'的属性'Preamble'的类型'System.ReadOnlySpan`1[System.Byte]'对于序列化或反序列化无效,因为它是指针类型、ref struct或包含未被特定类型替换的泛型参数。 Source=System.Text.Json StackTrace: at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_CannotSerializeInvalidType(Type typeToConvert, Type declaringType, MemberInfo memberInfo) at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.CreatePropertyInfo(JsonTypeInfo typeInfo, Type typeToConvert, MemberInfo memberInfo, JsonSerializerOptions options, Boolean shouldCheckForRequiredKeyword, Boolean hasJsonIncludeAttribute) at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.AddMembersDeclaredBySuperType(JsonTypeInfo typeInfo, Type currentType, Boolean constructorHasSetsRequiredMembersAttribute, PropertyHierarchyResolutionState& state) at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.PopulateProperties(JsonTypeInfo typeInfo) at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.CreateTypeInfoCore(Type type, JsonConverter converter, JsonSerializerOptions options) at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.GetTypeInfo(Type type, JsonSerializerOptions options) at System.Text.Json.JsonSerializerOptions.GetTypeInfoNoCaching(Type type) at System.Text.Json.JsonSerializerOptions.CachingContext.CreateCacheEntry(Type type, CachingContext context) --- End of stack trace from previous location --- at System.Text.Json.JsonSerializerOptions.GetTypeInfoInternal(Type type, Boolean ensureConfigured, Nullable`1 ensureNotNull, Boolean resolveIfMutable, Boolean fallBackToNearestAncestorType) at System.Text.Json.Serialization.Metadata.JsonPropertyInfo.Configure() at System.Text.Json.Serialization.Metadata.JsonTypeInfo.ConfigureProperties() at System.Text.Json.Serialization.Metadata.JsonTypeInfo.Configure() at System.Text.Json.Serialization.Metadata.JsonTypeInfo.<EnsureConfigured>g__ConfigureSynchronized|172_0() at System.Text.Json.JsonSerializerOptions.GetTypeInfoInternal(Type type, Boolean ensureConfigured, Nullable`1 ensureNotNull, Boolean resolveIfMutable, Boolean fallBackToNearestAncestorType) at System.Text.Json.JsonSerializerOptions.GetTypeInfoForRootType(Type type, Boolean fallBackToNearestAncestorType) at System.Text.Json.JsonSerializer.GetTypeInfo[T](JsonSerializerOptions options) at System.Text.Json.JsonSerializer.Serialize[TValue](TValue value, JsonSerializerOptions options) at TestJsonConverterBug.Program.TestAttributeSerialize() in ...\TestJsonConverterBug\Program.cs:line 35 at TestJsonConverterBug.Program.Main() in ...\TestJsonConverterBug\Program.cs:line 16
TestAttributeDeserialize异常
System.InvalidOperationException HResult=0x80131509 Message=类型'System.Text.Encoding'的属性'Preamble'的类型'System.ReadOnlySpan`1[System.Byte]'对于序列化或反序列化无效,因为它是指针类型、ref struct或包含未被特定类型替换的泛型参数。 Source=System.Text.Json StackTrace: at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_CannotSerializeInvalidType(Type typeToConvert, Type declaringType, MemberInfo memberInfo) at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.CreatePropertyInfo(JsonTypeInfo typeInfo, Type typeToConvert, MemberInfo memberInfo, JsonSerializerOptions options, Boolean shouldCheckForRequiredKeyword, Boolean hasJsonIncludeAttribute) at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.AddMembersDeclaredBySuperType(JsonTypeInfo typeInfo, Type currentType, Boolean constructorHasSetsRequiredMembersAttribute, PropertyHierarchyResolutionState& state) at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.PopulateProperties(JsonTypeInfo typeInfo) at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.CreateTypeInfoCore(Type type, JsonConverter converter, JsonSerializerOptions options) at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.GetTypeInfo(Type type, JsonSerializerOptions options) at System.Text.Json.JsonSerializerOptions.GetTypeInfoNoCaching(Type type) at System.Text.Json.JsonSerializerOptions.CachingContext.CreateCacheEntry(Type type, CachingContext context) --- End of stack trace from previous location --- at System.Text.Json.JsonSerializerOptions.GetTypeInfoInternal(Type type, Boolean ensureConfigured, Nullable`1 ensureNotNull, Boolean resolveIfMutable, Boolean fallBackToNearestAncestorType) at System.Text.Json.Serialization.Metadata.JsonPropertyInfo.Configure() at System.Text.Json.Serialization.Metadata.JsonTypeInfo.ConfigureProperties() at System.Text.Json.Serialization.Metadata.JsonTypeInfo.Configure() at System.Text.Json.Serialization.Metadata.JsonTypeInfo.<EnsureConfigured>g__ConfigureSynchronized|172_0() at System.Text.Json.JsonSerializerOptions.GetTypeInfoInternal(Type type, Boolean ensureConfigured, Nullable`1 ensureNotNull, Boolean resolveIfMutable, Boolean fallBackToNearestAncestorType) at System.Text.Json.JsonSerializerOptions.GetTypeInfoForRootType(Type type, Boolean fallBackToNearestAncestorType) at System.Text.Json.JsonSerializer.GetTypeInfo[T](JsonSerializerOptions options) at System.Text.Json.JsonSerializer.Deserialize[TValue](String json, JsonSerializerOptions options) at TestJsonConverterBug.Program.TestAttributeDeserialize() in ...\TestJsonConverterBug\Program.cs:line 45 at TestJsonConverterBug.Program.Main() in ...\TestJsonConverterBug\Program.cs:line 17
当在属性上使用[JsonConverter(typeof(JsonEncodingConverter))]时,System.Text.Json会尝试为Encoding类型构建序列化元数据(扫描其所有公共属性),但Encoding类中的Preamble属性是ReadOnlySpan<byte>类型——这是一个ref struct,不支持序列化,因此触发异常。
而通过JsonSerializerOptions.Converters添加转换器时,该转换器会被注册为Encoding类型的全局转换器,此时序列化逻辑会直接委托转换器处理整个对象,不会扫描Encoding的内部属性,因此不会触发错误。
方法1:改用转换器工厂
将原有的JsonEncodingConverter改为转换器工厂,这样属性级应用时,序列化逻辑会直接使用转换器处理对象,不会尝试生成Encoding的类型元数据。
修改后的转换器代码:
internal class JsonEncodingConverterFactory : JsonConverterFactory { public override bool CanConvert(Type typeToConvert) => typeToConvert.IsAssignableTo(typeof(Encoding)); public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => new JsonEncodingConverter(); private class JsonEncodingConverter : JsonConverter<Encoding> { public override Encoding? Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options ) => Encoding.GetEncoding(reader.GetString()!); public override void Write( Utf8JsonWriter writer, Encoding value, JsonSerializerOptions options ) => writer.WriteStringValue(value.EncodingName); } }
然后修改属性上的标记:
[JsonConverter(typeof(JsonEncodingConverterFactory))] public Encoding Encoding { get; set; }
方法2:包装属性为字符串
不使用转换器,而是将Encoding属性包装为可序列化的字符串属性,通过getter和setter完成类型转换:
internal class SerializableClass2 { public SerializableClass2() { Encoding = Encoding.ASCII; } [JsonIgnore] public Encoding Encoding { get; set; } [JsonPropertyName("Encoding")] public string EncodingName { get => Encoding.EncodingName; set => Encoding = Encoding.GetEncoding(value); } }
方法3:继续使用全局转换器
如果不需要属性级的转换器标记,保持原有的JsonEncodingConverter,继续通过JsonSerializerOptions.Converters添加转换器即可正常工作。
内容的提问来源于stack exchange,提问作者Ruafel

