You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 22:27:46