C#使用Newtonsoft Json反序列化含指定前缀动态键的JSON对象
方案1:使用[JsonExtensionData]原生特性(推荐,实现成本最低)
Newtonsoft.Json自带的[JsonExtensionData]特性可以自动把所有未在实体类中显式定义的键值对存入指定字典,你只需要后续对字典内容按前缀分组即可。
实体类定义:
public class LocalizedItem { // 固定字段显式定义 [JsonProperty("id")] public string Id { get; set; } // 存放所有未匹配的动态字段 [JsonExtensionData] private Dictionary<string, JToken> _dynamicFields; // 按前缀分组后的属性,懒加载避免重复计算 private Dictionary<string, List<string>> _descriptions; public Dictionary<string, List<string>> Descriptions { get { if (_descriptions == null) { _descriptions = _dynamicFields .Where(kv => kv.Key.StartsWith("descriptions_")) .ToDictionary( kv => kv.Key["descriptions_".Length..], // 截取后缀作为语言标识 kv => kv.Value.ToObject<List<string>>() ); } return _descriptions; } } private Dictionary<string, string> _officialTitles; public Dictionary<string, string> OfficialTitles { get { if (_officialTitles == null) { _officialTitles = _dynamicFields .Where(kv => kv.Key.StartsWith("official_title_")) .ToDictionary( kv => kv.Key["official_title_".Length..], kv => kv.Value.ToObject<string>() ); } return _officialTitles; } } private Dictionary<string, string> _authTowns; public Dictionary<string, string> AuthTowns { get { if (_authTowns == null) { _authTowns = _dynamicFields .Where(kv => kv.Key.StartsWith("auth_town_")) .ToDictionary( kv => kv.Key["auth_town_".Length..], kv => kv.Value.ToObject<string>() ); } return _authTowns; } } }
使用方式:
直接按常规方式反序列化即可,无需额外配置:
var result = JsonConvert.DeserializeObject<LocalizedItem>(yourJsonString); // 取值示例:获取bg语言的官方标题 var bgTitle = result.OfficialTitles["bg"];
方案2:自定义JsonConverter(适合需要序列化回原结构、或有更多解析自定义需求的场景)
如果需要完全控制解析逻辑,或者需要把实体序列化为原来的带语言后缀的JSON结构,可以自定义转换器实现。
1. 简化实体类定义:
public class LocalizedItem { public string Id { get; set; } // Key为语言后缀,Value为对应内容 public Dictionary<string, List<string>> Descriptions { get; set; } public Dictionary<string, string> OfficialTitles { get; set; } public Dictionary<string, string> AuthTowns { get; set; } }
2. 实现自定义转换器:
public class LocalizedItemConverter : JsonConverter<LocalizedItem> { public override LocalizedItem ReadJson(JsonReader reader, Type objectType, LocalizedItem existingValue, bool hasExistingValue, JsonSerializer serializer) { var jObj = JObject.Load(reader); var item = new LocalizedItem { Id = jObj["id"]?.ToString(), Descriptions = new Dictionary<string, List<string>>(), OfficialTitles = new Dictionary<string, string>(), AuthTowns = new Dictionary<string, string>() }; foreach (var prop in jObj.Properties()) { if (prop.Name.StartsWith("descriptions_")) { var lang = prop.Name["descriptions_".Length..]; item.Descriptions[lang] = prop.Value.ToObject<List<string>>(); } else if (prop.Name.StartsWith("official_title_")) { var lang = prop.Name["official_title_".Length..]; item.OfficialTitles[lang] = prop.Value.ToString(); } else if (prop.Name.StartsWith("auth_town_")) { var lang = prop.Name["auth_town_".Length..]; item.AuthTowns[lang] = prop.Value.ToString(); } } return item; } public override void WriteJson(JsonWriter writer, LocalizedItem value, JsonSerializer serializer) { var jObj = new JObject { { "id", value.Id } }; // 把字典序列化为带前缀的键 foreach (var desc in value.Descriptions) { jObj.Add($"descriptions_{desc.Key}", JToken.FromObject(desc.Value)); } foreach (var title in value.OfficialTitles) { jObj.Add($"official_title_{title.Key}", title.Value); } foreach (var town in value.AuthTowns) { jObj.Add($"auth_town_{town.Key}", town.Value); } jObj.WriteTo(writer); } }
使用方式:
反序列化时传入转换器即可:
var result = JsonConvert.DeserializeObject<LocalizedItem>(yourJsonString, new LocalizedItemConverter()); // 序列化回原JSON结构示例 var outputJson = JsonConvert.SerializeObject(result, new LocalizedItemConverter());
内容的提问来源于stack exchange,提问作者perene
相关产品推荐
相关产品推荐

