如何在C#类中定义以实现JSON重复字段名序列化
解决eBay API请求体序列化:重复字段而非数组的问题
嘿,我明白你的痛点——默认的JSON序列化会把集合转成数组,但eBay的API要求重复的outputSelector字段,而不是outputSelector[]数组。我来帮你搞定这个,顺便给你的代码提些符合C#规范的改进建议。
核心问题分析
大多数JSON序列化库(比如Newtonsoft.Json或System.Text.Json)默认会把List<T>/数组序列化为JSON数组,但eBay的这个API格式要求多个同名的顶级字段。所以我们需要自定义序列化逻辑,把集合里的每个元素都输出为独立的outputSelector键值对。
方案一:使用Newtonsoft.Json(最常用)
首先,我们先修正你的类结构(符合C#命名规范,改用属性而非字段),然后添加自定义转换器来处理outputSelector的序列化。
1. 自定义JSON转换器
这个转换器会把List<string>序列化为多个同名字段,同时兼容反序列化(如果需要的话):
using Newtonsoft.Json; using System.Collections.Generic; using System; public class MultipleStringConverter : JsonConverter<List<string>> { public override void WriteJson(JsonWriter writer, List<string> value, JsonSerializer serializer) { if (value == null || value.Count == 0) return; // 遍历列表,每个元素输出为独立的outputSelector字段 foreach (var selector in value) { writer.WritePropertyName("outputSelector"); serializer.Serialize(writer, selector); } } public override List<string> ReadJson(JsonReader reader, Type objectType, List<string> existingValue, bool hasExistingValue, JsonSerializer serializer) { var selectors = new List<string>(); // 兼容三种反序列化场景:数组、单个字符串、多个同名字段 if (reader.TokenType == JsonToken.StartArray) { selectors = serializer.Deserialize<List<string>>(reader); } else if (reader.TokenType == JsonToken.String) { selectors.Add((string)reader.Value); } else if (reader.TokenType == JsonToken.PropertyName) { while (reader.TokenType == JsonToken.PropertyName && (string)reader.Value == "outputSelector") { reader.Read(); selectors.Add((string)reader.Value); reader.Read(); } } return selectors; } }
2. 修正后的C#类结构
我调整了命名规范(帕斯卡命名法),把字段改成属性,添加了必要的JsonProperty属性映射JSON键名,并且初始化集合避免空引用:
using Newtonsoft.Json; using System.Collections.Generic; public class EbaySearchRequestBody { [JsonProperty("findCompletedItemsRequest")] public FindCompletedItemsRequest FindCompletedItemsRequest { get; set; } = new FindCompletedItemsRequest(); public class FindCompletedItemsRequest { [JsonProperty("keywords")] public string Keywords { get; set; } [JsonProperty("itemFilter")] public List<ItemFilter> ItemFilters { get; set; } = new List<ItemFilter>(); [JsonProperty("sortOrder")] public string SortOrder { get; set; } [JsonProperty("paginationInput")] public PaginationInput PaginationInput { get; set; } = new PaginationInput(); // 使用自定义转换器处理重复字段 [JsonProperty("outputSelector")] [JsonConverter(typeof(MultipleStringConverter))] public List<string> OutputSelectors { get; set; } = new List<string>(); } public class PaginationInput { [JsonProperty("entriesPerPage")] public string EntriesPerPage { get; set; } [JsonProperty("pageNumber")] public string PageNumber { get; set; } } public class ItemFilter { [JsonProperty("name")] public string Name { get; set; } [JsonProperty("value")] public string Value { get; set; } } }
3. 序列化示例代码
// 构建请求体 var searchBody = new EbaySearchRequestBody(); searchBody.FindCompletedItemsRequest.Keywords = "searchtext"; searchBody.FindCompletedItemsRequest.ItemFilters.Add(new EbaySearchRequestBody.ItemFilter { Name = "SoldItemsOnly", Value = "true" }); // 添加多个outputSelector searchBody.FindCompletedItemsRequest.OutputSelectors.Add("PictureURLLarge"); searchBody.FindCompletedItemsRequest.OutputSelectors.Add("SellerInfo"); searchBody.FindCompletedItemsRequest.PaginationInput.EntriesPerPage = "100"; searchBody.FindCompletedItemsRequest.PaginationInput.PageNumber = "1"; // 序列化JSON var json = JsonConvert.SerializeObject(searchBody, Formatting.Indented);
这样生成的JSON就会和你期望的完全一致:
{ "findCompletedItemsRequest": { "keywords": "searchtext", "itemFilter": [ { "name": "SoldItemsOnly", "value": "true" } ], "sortOrder": null, "paginationInput": { "entriesPerPage": "100", "pageNumber": "1" }, "outputSelector": "PictureURLLarge", "outputSelector": "SellerInfo" } }
方案二:使用System.Text.Json(.NET Core/.NET 5+)
如果你用的是原生的System.Text.Json,也可以写类似的自定义转换器:
using System.Collections.Generic; using System.Text.Json; using System.Text.Json.Serialization; using System; public class MultipleStringSystemTextJsonConverter : JsonConverter<List<string>> { public override List<string> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var selectors = new List<string>(); if (reader.TokenType == JsonTokenType.StartArray) { while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) { if (reader.TokenType == JsonTokenType.String) selectors.Add(reader.GetString()); } } else if (reader.TokenType == JsonTokenType.String) { selectors.Add(reader.GetString()); } else if (reader.TokenType == JsonTokenType.PropertyName) { while (reader.TokenType == JsonTokenType.PropertyName && reader.GetString() == "outputSelector") { reader.Read(); if (reader.TokenType == JsonTokenType.String) selectors.Add(reader.GetString()); reader.Read(); } } return selectors; } public override void Write(Utf8JsonWriter writer, List<string> value, JsonSerializerOptions options) { if (value == null || value.Count == 0) return; foreach (var selector in value) { writer.WriteString("outputSelector", selector); } } }
然后在类的OutputSelectors属性上替换为:
[JsonPropertyName("outputSelector")] [JsonConverter(typeof(MultipleStringSystemTextJsonConverter))] public List<string> OutputSelectors { get; set; } = new List<string>();
序列化时使用:
var json = JsonSerializer.Serialize(searchBody, new JsonSerializerOptions { WriteIndented = true });
额外的代码改进建议
- 命名规范:C#中类名、属性名推荐使用帕斯卡命名法(首字母大写),通过
JsonProperty/JsonPropertyName映射到JSON的驼峰命名,既符合.NET惯例又满足API要求。 - 避免空引用:初始化集合(
ItemFilters、OutputSelectors)和嵌套对象(FindCompletedItemsRequest、PaginationInput)为新实例,防止后续赋值时出现NullReferenceException。 - 类名语义化:把
eBaySearchBody改成EbaySearchRequestBody,更清晰表达它的用途;itemFilters改成ItemFilter(单个过滤器),列表属性命名为ItemFilters,符合语义。
内容的提问来源于stack exchange,提问作者Fr45h3R
相关产品推荐
相关产品推荐

