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

如何在.NET Native AOT项目中解析带引用的JSON文件?

在Native AOT项目中使用System.Text.Json源生成API解析带引用的JSON

我已经在Native AOT项目中实现了基于JsonSerializerContext的System.Text.Json源生成反序列化,但现在需要解析包含$ref引用的JSON,示例如下:

{
    "definitions": {
        "someProperty": {
            "type": "string"
        }
    },
    "properties": {
        "$ref": "#/definitions"
    }
}

根据文档,解析这类引用需要通过JsonSerializerOptions.ReferenceHandler配置保留引用的选项,但源生成模式下没有同时接收JsonSerializerOptions和JsonSerializerContext的Deserialize重载,且JsonSourceGenerationOptionsAttribute也没有对应的ReferenceHandler属性,请问这种情况下该怎么处理?


解决方案

方法1:通过上下文实例动态传入配置

虽然JsonSourceGenerationOptionsAttribute不支持直接设置ReferenceHandler,但可以在创建上下文实例时,传入自定义的JsonSerializerOptions来覆盖默认配置,这样既能保留源生成的性能优势,又能配置引用处理规则:

// 构建包含引用处理的配置
var options = new JsonSerializerOptions
{
    ReferenceHandler = ReferenceHandler.Preserve,
    // 同步源生成特性中配置的其他选项,比如WriteIndented等
    WriteIndented = true
};

// 用自定义配置初始化上下文
var context = new InterestingTypeSourceGenerationContext(options);
// 传入上下文进行反序列化
var result = JsonSerializer.Deserialize(jsonContent, typeof(YourTargetType), context);

方法2:自定义转换器手动处理引用

如果动态配置选项不符合需求,可以自定义JsonConverter来手动解析$ref引用逻辑:

public class ReferenceResolverConverter : JsonConverter<object>
{
    private JsonElement _rootElement;

    public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (reader.TokenType == JsonTokenType.StartObject)
        {
            using var doc = JsonDocument.ParseValue(ref reader);
            if (doc.RootElement.TryGetProperty("$ref", out var refElement))
            {
                var refPath = refElement.GetString();
                if (!string.IsNullOrEmpty(refPath) && refPath.StartsWith("#/"))
                {
                    var pathSegments = refPath.Substring(2).Split('/');
                    JsonElement current = _rootElement;
                    foreach (var segment in pathSegments)
                    {
                        if (!current.TryGetProperty(segment, out current))
                        {
                            throw new JsonException($"引用路径 {refPath} 不存在");
                        }
                    }
                    return JsonSerializer.Deserialize(current.GetRawText(), typeToConvert, options);
                }
            }
            return JsonSerializer.Deserialize(doc.RootElement.GetRawText(), typeToConvert, options);
        }
        return JsonSerializer.Deserialize(ref reader, typeToConvert, options);
    }

    public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
    {
        JsonSerializer.Serialize(writer, value, options);
    }

    // 初始化时传入整个JSON的根元素,用于解析引用路径
    public void Initialize(JsonElement rootElement)
    {
        _rootElement = rootElement;
    }
}

使用时先解析整个JSON文档获取根元素,再初始化转换器并完成反序列化:

using var doc = JsonDocument.Parse(jsonContent);
var converter = new ReferenceResolverConverter();
converter.Initialize(doc.RootElement);

var options = new JsonSerializerOptions
{
    Converters = { converter }
};

var context = new InterestingTypeSourceGenerationContext(options);
var result = JsonSerializer.Deserialize(jsonContent, typeof(YourTargetType), context);

注意事项

  • 方法1中,自定义JsonSerializerOptions需要和JsonSourceGenerationOptionsAttribute中配置的选项保持一致,避免反序列化行为不一致。
  • 方法2需要手动维护引用路径的解析逻辑,适合复杂或自定义的引用场景,但需要额外的代码维护成本。

内容的提问来源于stack exchange,提问作者Arieleo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 15:17:43