如何使用JSON.NET将JSON数组中的对象名转为属性值?
解决JSON动态键到C#固定类的反序列化问题
这个场景其实是JSON反序列化里很常见的「动态键映射固定类」需求,完全不用靠多态来硬凑,自定义一个JSON转换器就能完美保留代码的灵活性。下面我用两种主流的.NET JSON序列化库分别给出解决方案:
方案一:使用Newtonsoft.Json(Json.NET)
Newtonsoft.Json支持自定义转换器,我们可以通过实现JsonConverter<Statistic>来处理这种动态键的情况:
1. 实现自定义转换器
using Newtonsoft.Json; using Newtonsoft.Json.Linq; public class StatisticConverter : JsonConverter<Statistic> { public override Statistic ReadJson(JsonReader reader, Type objectType, Statistic existingValue, bool hasExistingValue, JsonSerializer serializer) { // 将当前JSON对象解析为JObject var jObject = JObject.Load(reader); // 获取唯一的键(题目说明所有键均唯一) var property = jObject.Properties().First(); var statistic = new Statistic { Name = property.Name, // 从属性值中读取min和max Min = property.Value["min"].Value<int>(), Max = property.Value["max"].Value<int>() }; return statistic; } public override void WriteJson(JsonWriter writer, Statistic value, JsonSerializer serializer) { // 如果需要将Statistic序列化为原JSON格式,实现这个方法 var jObject = new JObject(); var innerObject = new JObject { ["min"] = value.Min, ["max"] = value.Max }; jObject[value.Name] = innerObject; jObject.WriteTo(writer); } }
2. 在类中应用转换器
你可以直接在Container类的Statistics属性上标记转换器,或者在序列化/反序列化时指定:
public class Container { // 给数组属性添加转换器,指定每个元素用StatisticConverter处理 [JsonConverter(typeof(StatisticConverter))] public Statistic[] Statistics { get; set; } } public class Statistic { public string Name { get; set; } public int Min { get; set; } public int Max { get; set; } }
3. 测试反序列化
string json = @"{""statistics"": [ { ""Strength"": { ""min"": 16, ""max"": 20 } }, { ""Dexterity"": { ""min"": 16, ""max"": 20 } }]}"; Container container = JsonConvert.DeserializeObject<Container>(json); // 输出测试 foreach (var stat in container.Statistics) { Console.WriteLine($"Name: {stat.Name}, Min: {stat.Min}, Max: {stat.Max}"); } // 会输出: // Name: Strength, Min: 16, Max: 20 // Name: Dexterity, Min: 16, Max: 20
方案二:使用System.Text.Json(.NET Core/.NET 5+内置)
如果你用的是较新的.NET版本,也可以用内置的System.Text.Json实现自定义转换器:
1. 实现转换器
using System.Text.Json; using System.Text.Json.Serialization; public class StatisticSystemTextConverter : JsonConverter<Statistic> { public override Statistic Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { using (var jsonDoc = JsonDocument.ParseValue(ref reader)) { var root = jsonDoc.RootElement; // 获取唯一的属性 var property = root.EnumerateObject().First(); return new Statistic { Name = property.Name, Min = property.Value.GetProperty("min").GetInt32(), Max = property.Value.GetProperty("max").GetInt32() }; } } public override void Write(Utf8JsonWriter writer, Statistic value, JsonSerializerOptions options) { writer.WriteStartObject(); writer.WriteStartObject(value.Name); writer.WriteNumber("min", value.Min); writer.WriteNumber("max", value.Max); writer.WriteEndObject(); writer.WriteEndObject(); } }
2. 应用转换器
public class Container { [JsonConverter(typeof(StatisticSystemTextConverter))] public Statistic[] Statistics { get; set; } } // 或者在序列化选项中全局添加转换器 var options = new JsonSerializerOptions(); options.Converters.Add(new StatisticSystemTextConverter()); // 反序列化示例 Container container = JsonSerializer.Deserialize<Container>(json, options);
核心思路说明
两种方案的逻辑本质是一致的:
- 读取每个JSON数组元素中的单键对象
- 提取这个动态键作为
Statistic的Name属性 - 从键对应的子对象中读取
min和max值,填充到Statistic的对应字段
这种方式完全不需要为每种统计字段创建单独的类,不管后续新增多少种统计项(比如Luck、Intelligence),代码都能自动适配。
内容的提问来源于stack exchange,提问作者Vasilievski
相关产品推荐
相关产品推荐

