You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 19:50:54