C#中能否将JSON反序列化为泛型对象并实现动态属性名?
解决方案
针对Newtonsoft.Json的动态属性映射
可以通过自定义JsonConverter实现动态属性名映射,核心是根据泛型参数T的类型规则匹配API返回的属性名。
实现步骤
- 定义泛型转换器:
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); } }
- 使用转换器:
// 自动匹配复数属性名 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实现:
实现步骤
- 定义泛型转换器:
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(); } }
- 使用转换器:
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
相关产品推荐
相关产品推荐

