如何用C# System.Text.Json处理带请求标记的动态API响应
处理基于ItemType和Flags的动态API响应(System.Text.Json)
问题背景
使用C#及System.Text.Json对接第三方API时,SearchItemsRequestSpec.ItemType决定了响应的基础数据格式,SearchItemsRequest.Flags则控制响应中是否包含可选内容段。当前仅能处理固定类型的响应,无法适配这种动态结构变化,需要设计一套灵活的实体类和反序列化逻辑。
解决方案
1. 抽象通用响应结构
先抽离所有响应的公共字段,将Items定义为泛型,适配不同ItemType的实体:
public class SearchItemsResponse<T> { [JsonPropertyName("searchSpec")] public SearchItemsResponseSpec SearchSpec { get; set; } [JsonPropertyName("dataFlags")] public long DataFlags { get; set; } [JsonPropertyName("totalItemsCount")] public uint TotalItemsCount { get; set; } [JsonPropertyName("indexFrom")] public uint IndexFrom { get; set; } [JsonPropertyName("indexTo")] public uint IndexTo { get; set; } [JsonPropertyName("items")] public List<T> Items { get; set; } } // 公共响应规格类(匹配API返回的searchSpec字段) public class SearchItemsResponseSpec { [JsonPropertyName("itemsType")] public SearchItemsItemType ItemsType { get; set; } // 可根据API文档补充其他公共字段 }
2. 按ItemType拆分实体类
为每种ItemType创建独立的实体类,将可选内容段定义为可空属性,并通过JsonIgnoreCondition忽略空值序列化:
// Unit类型实体 public class Item_Unit { // BaseFlag对应的基础字段 [JsonPropertyName("id")] public long Id { get; set; } [JsonPropertyName("name")] public string Name { get; set; } // 可选字段:CustomProperties(对应Flags=2) [JsonPropertyName("custom_properties")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public UnitCustomProperties? CustomProperties { get; set; } // 可选字段:BillingProperties(对应Flags=4) [JsonPropertyName("billing")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public UnitBillingProperties? BillingProperties { get; set; } // 其他可选字段按此模式扩展... // 计算属性:解析DataFlags为枚举,方便判断可选字段是否存在 [JsonIgnore] public UnitResponseFlags ActiveFlags { get; set; } } // 可选字段对应的嵌套类 public class UnitCustomProperties { [JsonPropertyName("props")] public Dictionary<string, string> Props { get; set; } } public class UnitBillingProperties { [JsonPropertyName("balance")] public decimal Balance { get; set; } // 补充API返回的其他账单字段 }
3. 动态反序列化逻辑
根据请求的ItemType,选择对应的实体类型进行反序列化:
public object DeserializeSearchResponse(string json, SearchItemsItemType itemType) { return itemType switch { SearchItemsItemType.avl_unit => JsonSerializer.Deserialize<SearchItemsResponse<Item_Unit>>(json), SearchItemsItemType.avl_group => JsonSerializer.Deserialize<SearchItemsResponse<Item_Group>>(json), // 新增ItemType时,添加对应的分支即可 _ => throw new NotSupportedException($"不支持的ItemType:{itemType}") }; } // 使用示例 var request = new SearchItemsRequest(); request.Spec.ItemsType = SearchItemsItemType.avl_unit; request.Flags = (long)(UnitResponseFlags.BaseFlag | UnitResponseFlags.CustomProperties); // 发送请求获取响应字符串 string responseJson = await SendApiRequest(request); // 反序列化为对应类型 var unitResponse = (SearchItemsResponse<Item_Unit>)DeserializeSearchResponse(responseJson, request.Spec.ItemsType); // 标记当前激活的Flags,判断可选字段是否存在 foreach (var unit in unitResponse.Items) { unit.ActiveFlags = (UnitResponseFlags)unitResponse.DataFlags; if (unit.ActiveFlags.HasFlag(UnitResponseFlags.CustomProperties)) { // 处理自定义属性逻辑 var customProps = unit.CustomProperties?.Props; } }
4. 扩展与维护注意事项
- 启用项目的Nullable引用类型(项目文件中添加
<Nullable>enable</Nullable>),让可空属性的语义更清晰。 - 新增ItemType时,只需添加对应的实体类(如
Item_Group)和反序列化分支,无需修改核心逻辑。 - 若API返回的字段格式特殊,可自定义
JsonConverter处理复杂类型转换。
内容的提问来源于stack exchange,提问作者Hecatonchires
相关产品推荐
相关产品推荐

