如何在.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
相关产品推荐
相关产品推荐

