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

C#中能否将JSON反序列化为泛型对象并实现动态属性名?

解决方案

针对Newtonsoft.Json的动态属性映射

可以通过自定义JsonConverter实现动态属性名映射,核心是根据泛型参数T的类型规则匹配API返回的属性名。

实现步骤

  1. 定义泛型转换器:
public class DynamicDataConverter<T> : JsonConverter<ApiResponse<T>>
{
    private readonly string _targetPropertyName;

    // 默认用类型名复数形式匹配(可自定义规则)
    public DynamicDataConverter()
    {
        var typeName = typeof(T).Name;
        _targetPropertyName = typeName.EndsWith("s") ? typeName : typeName + "s";
    }

    // 支持手动指定属性名,适配特殊场景
    public DynamicDataConverter(string targetPropertyName)
    {
        _targetPropertyName = targetPropertyName;
    }

    public override ApiResponse<T> ReadJson(JsonReader reader, Type objectType, ApiResponse<T> existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        var jsonObject = JObject.Load(reader);
        var response = new ApiResponse<T>();

        // 映射固定属性
        response.StatusCode = jsonObject["StatusCode"]?.ToString();
        response.Message = jsonObject["Message"]?.ToString();

        // 映射动态数据属性
        response.Data = jsonObject[_targetPropertyName]?.ToObject<ICollection<T>>(serializer);

        return response;
    }

    public override void WriteJson(JsonWriter writer, ApiResponse<T> value, JsonSerializer serializer)
    {
        var jsonObject = new JObject();
        jsonObject.Add("StatusCode", value.StatusCode);
        jsonObject.Add("Message", value.Message);
        jsonObject.Add(_targetPropertyName, JToken.FromObject(value.Data, serializer));
        jsonObject.WriteTo(writer);
    }
}
  1. 使用转换器:
// 自动匹配复数属性名
var userResponse = JsonConvert.DeserializeObject<ApiResponse<User>>(jsonString, new DynamicDataConverter<User>());

// 手动指定属性名
var purchaseResponse = JsonConvert.DeserializeObject<ApiResponse<Purchase>>(jsonString, new DynamicDataConverter<Purchase>("Purchases"));

针对System.Text.Json的动态属性映射

逻辑和Newtonsoft.Json一致,通过自定义JsonConverter实现:

实现步骤

  1. 定义泛型转换器:
public class DynamicDataSystemTextJsonConverter<T> : JsonConverter<ApiResponse<T>>
{
    private readonly string _targetPropertyName;

    public DynamicDataSystemTextJsonConverter()
    {
        var typeName = typeof(T).Name;
        _targetPropertyName = typeName.EndsWith("s") ? typeName : typeName + "s";
    }

    public DynamicDataSystemTextJsonConverter(string targetPropertyName)
    {
        _targetPropertyName = targetPropertyName;
    }

    public override ApiResponse<T> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        using var jsonDoc = JsonDocument.ParseValue(ref reader);
        var root = jsonDoc.RootElement;

        var response = new ApiResponse<T>();
        if (root.TryGetProperty("StatusCode", out var statusCodeProp))
            response.StatusCode = statusCodeProp.GetString();
        if (root.TryGetProperty("Message", out var messageProp))
            response.Message = messageProp.GetString();
        if (root.TryGetProperty(_targetPropertyName, out var dataProp))
            response.Data = JsonSerializer.Deserialize<ICollection<T>>(dataProp.GetRawText(), options);

        return response;
    }

    public override void Write(Utf8JsonWriter writer, ApiResponse<T> value, JsonSerializerOptions options)
    {
        writer.WriteStartObject();
        writer.WriteString("StatusCode", value.StatusCode);
        writer.WriteString("Message", value.Message);
        writer.WritePropertyName(_targetPropertyName);
        JsonSerializer.Serialize(writer, value.Data, options);
        writer.WriteEndObject();
    }
}
  1. 使用转换器:
var options = new JsonSerializerOptions();
options.Converters.Add(new DynamicDataSystemTextJsonConverter<User>());
var userResponse = JsonSerializer.Deserialize<ApiResponse<User>>(jsonString, options);

// 手动指定属性名
var purchaseOptions = new JsonSerializerOptions();
purchaseOptions.Converters.Add(new DynamicDataSystemTextJsonConverter<Purchase>("Purchases"));
var purchaseResponse = JsonSerializer.Deserialize<ApiResponse<Purchase>>(jsonString, purchaseOptions);

方案复杂度与选择建议

  • 上述方案能实现单泛型类适配多响应,但需要维护自定义转换器,还要考虑属性名映射规则的扩展性(比如特殊类型的复数、API属性名与类型名不匹配的情况)。
  • 如果当前应用规模不大、响应类型数量有限,编写单独的响应对象反而更直观低风险:每个类直接用JsonProperty(或JsonPropertyName)明确指定属性名,代码可读性更高,排查问题更简单。

内容的提问来源于stack exchange,提问作者Owen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 23:37:46