含点命名的可变数量JSON属性反序列化为C#对象列表的方法
这问题我之前也碰到过,带点分隔的动态属性确实反序列化起来有点麻烦,不过用Newtonsoft.Json或者System.Text.Json都有靠谱的解决方案,我给你整理几种可行的方案:
方案一:Newtonsoft.Json 自定义转换器(最灵活)
这种方式适合需要复用逻辑的场景,通过自定义JsonConverter来解析特殊格式的属性,把它们转换成你需要的键值对列表。
首先定义目标实体类:
public class KeyValuePairItem { public string Key { get; set; } public string Value { get; set; } }
然后实现自定义转换器:
public class KeyValueListConverter : JsonConverter<List<KeyValuePairItem>> { public override List<KeyValuePairItem> ReadJson(JsonReader reader, Type objectType, List<KeyValuePairItem> existingValue, bool hasExistingValue, JsonSerializer serializer) { var jObject = JObject.Load(reader); var result = new List<KeyValuePairItem>(); // 先获取count值,判断有多少组键值对 if (!int.TryParse(jObject["SET.Key.count"]?.ToString(), out int count)) return result; // 按索引配对Key和Value for (int i = 0; i < count; i++) { var key = jObject[$"SET.Key.{i}"]?.ToString(); var value = jObject[$"SET.Value.{i}"]?.ToString(); // 确保Key存在再添加(避免空键的情况) if (!string.IsNullOrEmpty(key)) { result.Add(new KeyValuePairItem { Key = key, Value = value }); } } return result; } public override void WriteJson(JsonWriter writer, List<KeyValuePairItem> value, JsonSerializer serializer) { // 如果需要把列表序列回原格式,可以在这里实现,暂时先抛出异常 throw new NotImplementedException("序列化逻辑未实现"); } }
使用示例:
// 你的JSON字符串 var json = @"{""data"": {""SET.Key.0"":""Key 1"",""SET.Key.1"":""Key 2"",""SET.Key.count"":""2"",""SET.Value.0"":""10"",""SET.Value.1"":""20"",""SET.Value.count"":""2""}}"; // 解析JSON并提取data节点 var jObject = JObject.Parse(json); var dataToken = jObject["data"]; // 使用自定义转换器反序列化 var keyValueList = dataToken.ToObject<List<KeyValuePairItem>>(new JsonSerializerSettings { Converters = new List<JsonConverter> { new KeyValueListConverter() } }); // 输出结果 foreach (var item in keyValueList) { Console.WriteLine($"Key: {item.Key}, Value: {item.Value}"); }
方案二:直接遍历JObject属性(快速实现)
如果只是临时处理,不想写自定义转换器,可以直接遍历JObject的属性,筛选出需要的Key和Value并配对:
var json = @"{""data"": {""SET.Key.0"":""Key 1"",""SET.Key.1"":""Key 2"",""SET.Key.count"":""2"",""SET.Value.0"":""10"",""SET.Value.1"":""20"",""SET.Value.count"":""2""}}"; var dataObject = JObject.Parse(json)["data"] as JObject; var keyValueList = new List<KeyValuePairItem>(); if (dataObject == null) return; // 筛选所有SET.Key开头且不是count的属性 var keyProperties = dataObject.Properties() .Where(p => p.Name.StartsWith("SET.Key.") && !p.Name.EndsWith(".count")) .ToList(); foreach (var keyProp in keyProperties) { // 提取索引(比如从SET.Key.0中拿到0) var index = keyProp.Name.Split('.').Last(); // 找到对应的SET.Value属性 var valueProp = dataObject.Properties().FirstOrDefault(p => p.Name == $"SET.Value.{index}"); if (valueProp != null) { keyValueList.Add(new KeyValuePairItem { Key = keyProp.Value.ToString(), Value = valueProp.Value.ToString() }); } }
这种方式代码更简洁,适合一次性处理的场景。
方案三:System.Text.Json 自定义转换器(换库备选)
如果你想换成微软官方的System.Text.Json,思路和Newtonsoft类似,也是自定义转换器:
public class KeyValueListSystemTextJsonConverter : JsonConverter<List<KeyValuePairItem>> { public override List<KeyValuePairItem> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { using var doc = JsonDocument.ParseValue(ref reader); var root = doc.RootElement; var result = new List<KeyValuePairItem>(); // 获取count值 if (!root.TryGetProperty("SET.Key.count", out var countElement) || !int.TryParse(countElement.GetString(), out int count)) return result; // 配对键值对 for (int i = 0; i < count; i++) { if (root.TryGetProperty($"SET.Key.{i}", out var keyElement) && root.TryGetProperty($"SET.Value.{i}", out var valueElement)) { result.Add(new KeyValuePairItem { Key = keyElement.GetString(), Value = valueElement.GetString() }); } } return result; } public override void Write(Utf8JsonWriter writer, List<KeyValuePairItem> value, JsonSerializerOptions options) { throw new NotImplementedException("序列化逻辑未实现"); } }
使用示例:
var json = @"{""SET.Key.0"":""Key 1"",""SET.Key.1"":""Key 2"",""SET.Key.count"":""2"",""SET.Value.0"":""10"",""SET.Value.1"":""20"",""SET.Value.count"":""2""}"; var options = new JsonSerializerOptions { Converters = { new KeyValueListSystemTextJsonConverter() } }; var keyValueList = JsonSerializer.Deserialize<List<KeyValuePairItem>>(json, options);
额外注意事项
- 处理count不一致的情况:如果JSON中
SET.Key.count和SET.Value.count的值不一样,你可以在代码中添加判断逻辑(比如抛出异常或者取较小的那个值),避免出现索引越界的问题。 - 类型转换:如果Value是数字类型(比如int、decimal),可以修改
KeyValuePairItem的Value属性类型,然后在解析的时候做类型转换(比如int.Parse(valueProp.Value.ToString()))。 - 前缀可配置:如果前缀不是固定的
SET,可以把前缀做成转换器的参数,让逻辑更通用。
内容的提问来源于stack exchange,提问作者Louis
相关产品推荐
相关产品推荐

