如何获取JsonPolymorphic中未匹配派生类型的TypeDiscriminator值?
如何获取System.Text.Json中未匹配派生类的多态鉴别器值?
当你使用System.Text.Json的JsonPolymorphic特性,将Operation作为类型鉴别器并设置IgnoreUnrecognizedTypeDiscriminators = true时,未匹配任何派生类的对象会被反序列化为基类实例,但基类的Operation属性默认会是null——这是因为默认的多态解析逻辑不会将鉴别器值绑定到基类属性。你可以通过以下两种方法解决这个问题:
方法一:利用JsonExtensionData属性(最简单)
在基类中添加[JsonExtensionData]标记的字典属性,所有未被显式映射的JSON属性(包括未匹配的鉴别器Operation)都会被自动存入这个字典,之后你可以从中提取鉴别器值。
代码示例
[JsonPolymorphic(TypeDiscriminatorPropertyName = "Operation", IgnoreUnrecognizedTypeDiscriminators = true)] [JsonDerivedType(typeof(UserCreatedEntry), typeDiscriminator: "UserCreated")] [JsonDerivedType(typeof(UserDeletedEntry), typeDiscriminator: "UserDeleted")] public class AuditLogEntry { public DateTime Timestamp { get; set; } // 存储所有未识别的JSON属性 [JsonExtensionData] public Dictionary<string, JsonElement> ExtraData { get; set; } = new(); // 封装获取Operation的逻辑 public string? GetOperation() { if (ExtraData.TryGetValue("Operation", out var opElement)) { return opElement.GetString(); } return null; } } public class UserCreatedEntry : AuditLogEntry { public string Username { get; set; } = string.Empty; } public class UserDeletedEntry : AuditLogEntry { public Guid UserId { get; set; } }
使用方式
反序列化后,直接调用基类的GetOperation()方法即可获取未匹配的鉴别器值:
var json = "{\"Operation\": \"UnknownAction\", \"Timestamp\": \"2024-01-01T00:00:00\"}"; var entry = JsonSerializer.Deserialize<AuditLogEntry>(json); string? unknownOperation = entry?.GetOperation(); // 输出 "UnknownAction"
方法二:自定义JsonConverter(更灵活)
如果需要直接将鉴别器值赋值给基类的Operation属性,可以自定义转换器,在解析过程中先读取鉴别器值,再完成反序列化,最后为基类实例赋值。
代码示例
public class AuditLogEntryConverter : JsonConverter<AuditLogEntry> { private readonly JsonConverter<AuditLogEntry> _defaultPolymorphicConverter; public AuditLogEntryConverter(JsonSerializerOptions options) { // 获取默认的多态转换器,保留原有解析逻辑 _defaultPolymorphicConverter = (JsonConverter<AuditLogEntry>)options.GetConverter(typeof(AuditLogEntry)); } public override AuditLogEntry? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { // 复制读取器状态,避免影响默认转换器的解析位置 var cloneReader = reader; string? operation = null; // 提前读取Operation属性值 if (cloneReader.TokenType == JsonTokenType.StartObject) { while (cloneReader.Read()) { if (cloneReader.TokenType == JsonTokenType.EndObject) break; if (cloneReader.TokenType == JsonTokenType.PropertyName && cloneReader.GetString() == "Operation") { cloneReader.Read(); operation = cloneReader.GetString(); break; } cloneReader.Skip(); // 跳过其他属性,不影响后续解析 } } // 使用默认转换器完成对象反序列化 var entry = _defaultPolymorphicConverter.Read(ref reader, typeToConvert, options); // 仅当实例为基类(未匹配派生类)时,赋值Operation属性 if (entry?.GetType() == typeof(AuditLogEntry) && operation != null) { entry.Operation = operation; } return entry; } public override void Write(Utf8JsonWriter writer, AuditLogEntry value, JsonSerializerOptions options) { // 复用默认转换器的序列化逻辑 _defaultPolymorphicConverter.Write(writer, value, options); } }
使用方式
将自定义转换器添加到JsonSerializerOptions中:
var options = new JsonSerializerOptions(); options.Converters.Add(new AuditLogEntryConverter(options)); var json = "{\"Operation\": \"UnknownAction\", \"Timestamp\": \"2024-01-01T00:00:00\"}"; var entry = JsonSerializer.Deserialize<AuditLogEntry>(json, options); string? unknownOperation = entry?.Operation; // 输出 "UnknownAction"
两种方法对比
- 扩展数据法:代码量少,实现简单,适合快速需求;但需要通过字典间接获取鉴别器值。
- 自定义转换器法:可以直接将鉴别器值绑定到基类属性,逻辑更直观;但需要额外编写转换器代码,维护成本稍高。
内容的提问来源于stack exchange,提问作者sschoof
相关产品推荐
相关产品推荐

