如何将Newtonsoft JsonConverter迁移至System.Text.Json的JsonConverter<T>
问题描述
我定义了如下接口:
public interface IApiResult<TResult> : IApiResult { TResult Result { get; set; } }
以及对应的具体实现类:
public class ApiResult<TResult> : ApiResult, IApiResult<TResult> { public ApiResult( TResult result ) : base() { Result = result; } public TResult Result { get; set; } }
此前使用Newtonsoft.Json库时,我通过如下JsonConverter实现多态序列化与反序列化:
public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer ) { var obj = JObject.Load( reader ); var apiResult = new ApiResult.ApiResult().As<IApiResult>(); //Check if is an IApiResult<T> if (obj.TryGetValue( nameof( ApiResult<object>.Result ), StringComparison.InvariantCultureIgnoreCase, out var jResult )) { //Retrieve the result property type in order to create the proper apiResult object var prop = objectType.GetProperty( nameof( ApiResult<object>.Result ) ); var value = jResult.ToObject( prop.PropertyType, serializer ); var rType = typeof( ApiResult<> ).MakeGenericType( prop.PropertyType ); apiResult = Activator.CreateInstance( rType ).As<IApiResult>(); prop.SetValue( apiResult, value ); } //Set the messages var jMessages = obj.GetValue( nameof( ApiResult.ApiResult.Messages ), StringComparison.InvariantCultureIgnoreCase ).ToObject<JObject[]>(); apiResult.Messages = DeserializeReasons( jMessages ); return apiResult; }
现在需要将这段代码迁移至System.Text.Json,最大的问题在于JObject.TryGetValue方法——它能直接返回反序列化后的对象,让我识别类型并通过反射确定ApiResult<T>的类型,但当前的UTF8JsonReader只能逐令牌读取,无法复现此前的行为。
解决方案
方法一:使用JsonNode(.NET 6及以上版本推荐)
JsonNode是System.Text.Json在.NET 6中新增的API,功能和Newtonsoft的JObject类似,可直接加载整个JSON并按需访问属性,完美替代原有逻辑:
public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var jsonNode = JsonNode.Parse(ref reader); if (jsonNode is not JsonObject jsonObj) { throw new JsonException("Invalid JSON format for IApiResult"); } IApiResult apiResult = new ApiResult(); var resultPropName = nameof(ApiResult<object>.Result); // 检查并处理Result属性(忽略大小写) if (jsonObj.TryGetPropertyValue(resultPropName, StringComparison.InvariantCultureIgnoreCase, out var resultNode)) { var resultProp = typeToConvert.GetProperty(resultPropName); if (resultProp == null) { throw new InvalidOperationException($"Type {typeToConvert.Name} does not contain a {resultPropName} property"); } // 反序列化Result值 var resultValue = resultNode.Deserialize(resultProp.PropertyType, options); // 创建对应的ApiResult<T>实例 var genericType = typeof(ApiResult<>).MakeGenericType(resultProp.PropertyType); apiResult = (IApiResult)Activator.CreateInstance(genericType); resultProp.SetValue(apiResult, resultValue); } // 处理Messages属性 var messagesPropName = nameof(ApiResult.Messages); if (jsonObj.TryGetPropertyValue(messagesPropName, StringComparison.InvariantCultureIgnoreCase, out var messagesNode)) { var messagesArray = messagesNode.Deserialize<JsonElement[]>(options); apiResult.Messages = DeserializeReasons(messagesArray); } return apiResult; }
方法二:使用JsonDocument(兼容.NET 5及更早版本)
如果项目仍在使用.NET 5或更早版本,可通过JsonDocument读取整个JSON内容后解析,逻辑和原有JObject用法对齐:
public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { using var doc = JsonDocument.ParseValue(ref reader); var root = doc.RootElement; IApiResult apiResult = new ApiResult(); var resultPropName = nameof(ApiResult<object>.Result); // 查找Result属性(忽略大小写) if (root.TryGetProperty(resultPropName, StringComparison.InvariantCultureIgnoreCase, out var resultElement)) { var resultProp = typeToConvert.GetProperty(resultPropName); if (resultProp == null) { throw new InvalidOperationException($"Type {typeToConvert.Name} does not contain a {resultPropName} property"); } // 反序列化Result值 var resultValue = JsonSerializer.Deserialize(resultElement, resultProp.PropertyType, options); // 创建ApiResult<T>实例 var genericType = typeof(ApiResult<>).MakeGenericType(resultProp.PropertyType); apiResult = (IApiResult)Activator.CreateInstance(genericType); resultProp.SetValue(apiResult, resultValue); } // 处理Messages属性 var messagesPropName = nameof(ApiResult.Messages); if (root.TryGetProperty(messagesPropName, StringComparison.InvariantCultureIgnoreCase, out var messagesElement)) { var messagesArray = JsonSerializer.Deserialize<JsonElement[]>(messagesElement, options); apiResult.Messages = DeserializeReasons(messagesArray); } return apiResult; }
注意事项
- 需调整
DeserializeReasons方法的入参类型,适配JsonElement[]或JsonNode[](原方法接收JObject数组)。 - 若需要实现序列化逻辑(Write方法),可反向使用JsonNode/JsonDocument将ApiResult实例的属性写入JSON。
- .NET 5及更早版本的
TryGetPropertyValue不支持StringComparison参数,需手动遍历属性名进行大小写匹配。
内容的提问来源于stack exchange,提问作者Lorenzo
相关产品推荐
相关产品推荐

