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

使用Newtonsoft.Json根据属性特性中指定的类型动态序列化/反序列化属性名称

Newtonsoft.Json根据属性特性中指定的类型动态序列化/反序列化属性名称

我完全懂你的困扰——用XSD生成的C#类里有个Item属性,挂了一堆XmlElementAttribute,每个都对应不同的类型和XML元素名,但Newtonsoft.Json序列化时只会固定输出"Item"这个键,你想让它根据实际赋值的类型自动换成特性里指定的名称,还得保留其他属性原有的Json.NET特性处理对吧?

你之前尝试的ContractResolver和JsonConverter思路方向都对,只是没把两者结合好。这里给你一个可行的方案,既不用重写所有序列化逻辑,又能完美实现动态属性名的需求:

第一步:提取类型与目标名称的映射关系

首先我们需要从Item属性的特性里,把每个类型对应的目标Json键名提取出来,做一个映射表:

private static Dictionary<Type, string> GetTypeToNameMap(MemberInfo member)
{
    var typeNameMap = new Dictionary<Type, string>();
    var xmlAttributes = member.GetCustomAttributes<XmlElementAttribute>(inherit: false);
    
    foreach (var attr in xmlAttributes)
    {
        if (attr.Type != null && !string.IsNullOrEmpty(attr.ElementName))
        {
            typeNameMap[attr.Type] = attr.ElementName;
        }
    }
    return typeNameMap;
}

第二步:自定义JsonConverter处理动态序列化/反序列化

接下来写一个专门处理Item属性的转换器,它会根据实际对象的类型查找对应的键名,同时复用Newtonsoft的默认序列化逻辑——这样你原来加在各个请求类型上的JsonProperty、JsonIgnore等特性都能正常生效,不用手动重新实现:

public class DynamicItemConverter : JsonConverter
{
    private readonly Dictionary<Type, string> _typeToNameMap;
    private readonly Dictionary<string, Type> _nameToTypeMap;

    public DynamicItemConverter(MemberInfo member)
    {
        _typeToNameMap = GetTypeToNameMap(member);
        _nameToTypeMap = _typeToNameMap.ToDictionary(kv => kv.Value, kv => kv.Key);
    }

    public override bool CanConvert(Type objectType)
    {
        // 只处理object类型的属性(或者你实际的基类型)
        return objectType == typeof(object);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        if (value == null)
        {
            writer.WriteNull();
            return;
        }

        var actualType = value.GetType();
        if (_typeToNameMap.TryGetValue(actualType, out var targetName))
        {
            // 用特性里的名称作为键,序列化对象内容
            writer.WriteStartObject();
            writer.WritePropertyName(targetName);
            serializer.Serialize(writer, value);
            writer.WriteEndObject();
        }
        else
        {
            // 找不到对应类型时的降级处理,比如按默认逻辑序列化或抛出异常
            serializer.Serialize(writer, value);
        }
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;

        // 读取Json对象,找到唯一的属性名
        var jObject = JObject.Load(reader);
        var targetProperty = jObject.Properties().FirstOrDefault();
        if (targetProperty == null)
            return null;

        if (_nameToTypeMap.TryGetValue(targetProperty.Name, out var targetType))
        {
            // 根据属性名找到对应类型,反序列化
            return targetProperty.Value.ToObject(targetType, serializer);
        }
        else
        {
            // 找不到对应类型时的处理,比如返回null或抛出异常
            return null;
        }
    }
}

第三步:给Item属性绑定转换器(两种方式可选)

方式一:用ContractResolver自动绑定(推荐,无需修改生成的类)

如果你不想改动XSD生成的类,可以写一个自定义ContractResolver,自动给Item属性加上转换器:

public class DynamicItemContractResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var jsonProperty = base.CreateProperty(member, memberSerialization);

        // 只处理名为"Item"的属性,也可以根据其他条件筛选
        if (member.Name == "Item" && member.MemberType == MemberTypes.Property)
        {
            jsonProperty.Converter = new DynamicItemConverter(member);
            // 让转换器全权处理该属性的序列化,避免默认逻辑干扰
            jsonProperty.ShouldSerialize = _ => false;
        }

        return jsonProperty;
    }
}

序列化时使用这个ContractResolver即可:

var serializerSettings = new JsonSerializerSettings
{
    ContractResolver = new DynamicItemContractResolver(),
    Formatting = Formatting.Indented // 保留你原来的其他配置
};

var jsonResult = JsonConvert.SerializeObject(yourInstance, serializerSettings);

方式二:直接给属性加特性(允许修改类时使用)

如果可以修改生成的类,也可以直接给Item属性添加转换器特性:

[JsonConverter(typeof(DynamicItemConverter), typeof(YourParentClass), "Item")]
// 保留原来的XmlElementAttribute
[XmlElementAttribute("ReportRequest", typeof(ReportRequestType), Form = XmlSchemaForm.Unqualified)]
[XmlElementAttribute("StatusRequest", typeof(StatusRequestType), Form = XmlSchemaForm.Unqualified)]
[XmlElementAttribute("TransmitRequest", typeof(TransmitRequestType), Form = XmlSchemaForm.Unqualified)]
public object Item { get; set; }

这时候需要调整转换器的构造函数,支持通过参数获取属性信息:

public DynamicItemConverter(Type parentType, string propertyName)
{
    var targetProperty = parentType.GetProperty(propertyName);
    _typeToNameMap = GetTypeToNameMap(targetProperty);
    _nameToTypeMap = _typeToNameMap.ToDictionary(kv => kv.Value, kv => kv.Key);
}

方案优势

  • 完全复用Newtonsoft的默认序列化逻辑,原有类型上的Json特性不受影响,不用重复造轮子
  • ContractResolver方式无需修改自动生成的类,避免了重新生成代码后手动修改的麻烦
  • 同时支持序列化和反序列化,完全覆盖你的需求场景

你可以根据自己的实际情况选择对应的方式,测试一下应该就能达到你想要的效果了。

备注:内容来源于stack exchange,提问作者philippe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.13 18:13:14