如何处理JSON中含动态日期属性的对象?附结构示例
碰到这种带动态日期后缀的JSON属性确实头疼,总不能每次新增日期都手动加属性对吧?我给你分享几个实用的解决方案,都是C#里处理这类动态JSON的常用手段:
方法一:利用Json扩展属性(推荐,简单高效)
不管你用的是Newtonsoft.Json(Json.NET)还是System.Text.Json,都支持扩展属性特性,能把那些未在类中定义的动态属性统一收集到一个字典里,之后我们再把这些属性转换成方便使用的日期-布尔值映射。
针对Newtonsoft.Json的实现
using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; public class RootObject { public string id { get; set; } public string name { get; set; } public string type { get; set; } // 这里保留你的其他固定属性(TypeBlock、AddressBlock等) // 用JsonExtensionData标记,收集所有未定义的属性 [JsonExtensionData] private IDictionary<string, JToken> _dynamicProperties; // 提供一个只读属性,把动态日期属性转换成易读的字典 public Dictionary<DateTime, bool?> ExceptionalDays { get { var exceptionalDays = new Dictionary<DateTime, bool?>(); foreach (var kvp in _dynamicProperties) { // 筛选出exceptionalDay开头的属性 if (kvp.Key.StartsWith("exceptionalDay_")) { // 提取日期字符串并转换格式 var datePart = kvp.Key.Replace("exceptionalDay_", "").Replace("_", "-"); if (DateTime.TryParse(datePart, out DateTime date)) { // 把JToken转换成bool?类型 exceptionalDays[date] = kvp.Value.ToObject<bool?>(); } } } return exceptionalDays; } } }
针对System.Text.Json的实现
System.Text.Json的扩展属性要求字典类型为Dictionary<string, JsonElement>,处理逻辑稍作调整:
using System; using System.Collections.Generic; using System.Text.Json; using System.Text.Json.Serialization; public class RootObject { public string id { get; set; } public string name { get; set; } public string type { get; set; } // 其他固定属性... [JsonExtensionData] private Dictionary<string, JsonElement> _dynamicProperties; public Dictionary<DateTime, bool?> ExceptionalDays { get { var exceptionalDays = new Dictionary<DateTime, bool?>(); foreach (var kvp in _dynamicProperties) { if (kvp.Key.StartsWith("exceptionalDay_")) { var datePart = kvp.Key.Replace("exceptionalDay_", "").Replace("_", "-"); if (DateTime.TryParse(datePart, out DateTime date)) { // 根据JsonElement的类型转换bool? exceptionalDays[date] = kvp.Value.ValueKind switch { JsonValueKind.True => true, JsonValueKind.False => false, JsonValueKind.Null => null, _ => null }; } } } return exceptionalDays; } } }
方法二:自定义JSON转换器(灵活可控)
如果需要更精细的控制(比如序列化时也要把字典转成动态日期属性),可以自定义一个JsonConverter,手动处理每个属性的读写逻辑:
using System; using System.Collections.Generic; using System.Text.Json; using System.Text.Json.Serialization; public class RootObjectConverter : JsonConverter<RootObject> { public override RootObject Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var root = new RootObject(); var exceptionalDays = new Dictionary<DateTime, bool?>(); while (reader.Read()) { if (reader.TokenType == JsonTokenType.EndObject) break; if (reader.TokenType == JsonTokenType.PropertyName) { var propName = reader.GetString(); reader.Read(); // 处理固定属性 switch (propName) { case "id": root.id = reader.GetString(); break; case "name": root.name = reader.GetString(); break; case "type": root.type = reader.GetString(); break; // 这里添加其他固定属性的处理逻辑 default: // 处理动态日期属性 if (propName.StartsWith("exceptionalDay_")) { var datePart = propName.Replace("exceptionalDay_", "").Replace("_", "-"); if (DateTime.TryParse(datePart, out DateTime date)) { bool? value = null; switch (reader.TokenType) { case JsonTokenType.True: value = true; break; case JsonTokenType.False: value = false; break; case JsonTokenType.Null: value = null; break; } exceptionalDays[date] = value; } } break; } } } root.ExceptionalDays = exceptionalDays; return root; } public override void Write(Utf8JsonWriter writer, RootObject value, JsonSerializerOptions options) { writer.WriteStartObject(); // 序列化固定属性 writer.WriteString("id", value.id); writer.WriteString("name", value.name); writer.WriteString("type", value.type); // 其他固定属性... // 把ExceptionalDays转换成动态日期属性 foreach (var kvp in value.ExceptionalDays) { var propName = $"exceptionalDay_{kvp.Key:yyyy_MM_dd}"; if (kvp.Value.HasValue) writer.WriteBoolean(propName, kvp.Value.Value); else writer.WriteNull(propName); } writer.WriteEndObject(); } } // 使用时需要在序列化/反序列化时指定转换器 // 比如: // var options = new JsonSerializerOptions(); // options.Converters.Add(new RootObjectConverter()); // var root = JsonSerializer.Deserialize<RootObject>(jsonStr, options);
选择建议
- 如果只是需要反序列化读取数据,方法一(扩展属性)足够简单,代码量少,推荐优先使用。
- 如果需要双向序列化(把C#对象转回原格式的JSON)或者有特殊的属性处理逻辑,方法二的自定义转换器更灵活。
内容的提问来源于stack exchange,提问作者xdtTransform
相关产品推荐
相关产品推荐

