从Cosmos DB读取时NuGet.Versioning.VersionRange构造函数找不到错误
问题:Cosmos DB读取VersionRange类型时反序列化失败
我创建了依赖NuGet.Versioning中VersionRange类的DependencyDto,为在Swagger UI里将VersionRange以字符串展示,给该属性实现了自定义JsonConverter。同时创建了未添加JsonConverter特性的DependencyDoc类,通过AutoMapper映射后存入Cosmos DB,写入操作正常,但从Cosmos DB读取响应并转换为DependencyDoc类时,出现构造函数找不到的错误,提示VersionRange类需有默认构造函数、带参构造函数或标记JsonConstructor特性。
相关代码
DependencyDto 代码
public class DependencyDto { public string Name { get; set; } [JsonProperty("VersionRange")] [JsonConverter(typeof(VersionRangeConverter))] public VersionRange VersionRange { get; set; } }
自定义JsonConverter代码
public class VersionRangeConverter : JsonConverter<VersionRange> { public override void WriteJson(JsonWriter writer, VersionRange value, JsonSerializer serializer) { writer.WriteValue(value.ToString()); } public override VersionRange ReadJson(JsonReader reader, Type objectType, VersionRange existingValue, bool hasExistingValue, JsonSerializer serializer) { Requires.NotNull(serializer, nameof(JsonSerializer)); VersionRange versionRange = null; var versionRangeObject = serializer.Deserialize(reader); if (versionRangeObject is null) { return null; } else if (versionRangeObject is string) { if (!VersionRange.TryParse(versionRangeObject.ToString(), out versionRange)) { throw new Exception(); } } else { try { dynamic ver = JsonConvert.DeserializeObject(versionRangeObject.ToString()); NuGetVersion maxVersion = versionRange.MaxVersion; NuGetVersion minVersion = versionRange.MinVersion; bool isMinInclusive = versionRange.IsMinInclusive; bool isMaxInclusive = versionRange.IsMaxInclusive; string originalString = versionRange.OriginalString; versionRange = new VersionRange(minVersion, isMinInclusive, maxVersion, isMaxInclusive, null, originalString); } catch { throw; } } return versionRange; } }
Cosmos DB中存储的文档片段
"Dependencies": [ { "Name": "string", "VersionRange": { "IsFloating": false, "Float": null, "OriginalString": "[1.0.0, 2.0.0)", "HasLowerBound": true, "HasUpperBound": true, "HasLowerAndUpperBounds": true, "IsMinInclusive": true, "IsMaxInclusive": false, "MaxVersion": "2.0.0", "MinVersion": "1.0.0" } } ]
读取转换代码
ResourceResponse<Document> response = await this.DocumentDbClient.WriteAsync(doc.Id, this.CollectionName, doc).ConfigureAwait(false); DependencyDoc dependencyDoc= (DependencyDoc)(dynamic)response.Resource;
错误信息
"Message": "Unable to find a constructor to use for type NuGet.Versioning.VersionRange. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute. Path 'Module.Dependencies[0].VersionRange.IsFloating', line 1, position 285."
解决方案
方案1:给DependencyDoc的VersionRange属性添加JsonConverter
DependencyDoc读取时未指定转换器,Json.NET会尝试直接反序列化VersionRange对象,但该类没有公开默认构造函数或标记JsonConstructor的构造函数,导致失败。只需给DependencyDoc的对应属性加上相同的转换器特性:
public class DependencyDoc { public string Name { get; set; } [JsonProperty("VersionRange")] [JsonConverter(typeof(VersionRangeConverter))] public VersionRange VersionRange { get; set; } }
方案2:修复自定义转换器的ReadJson逻辑
当前ReadJson处理对象类型时存在逻辑错误:尝试从versionRange(此时为null)读取属性值会引发空引用异常,应从反序列化后的dynamic对象中获取属性。同时优先用OriginalString解析,更可靠:
public override VersionRange ReadJson(JsonReader reader, Type objectType, VersionRange existingValue, bool hasExistingValue, JsonSerializer serializer) { Requires.NotNull(serializer, nameof(JsonSerializer)); var token = JToken.Load(reader); if (token.Type == JTokenType.Null) { return null; } else if (token.Type == JTokenType.String) { if (!VersionRange.TryParse(token.ToString(), out var versionRange)) { throw new InvalidOperationException("无法解析版本范围字符串"); } return versionRange; } else { try { var originalString = token["OriginalString"].ToString(); // 优先通过原始字符串解析 if (VersionRange.TryParse(originalString, out var versionRange)) { return versionRange; } // 备用:从各属性构造 var minVersion = token["MinVersion"] != null ? NuGetVersion.Parse(token["MinVersion"].ToString()) : null; var maxVersion = token["MaxVersion"] != null ? NuGetVersion.Parse(token["MaxVersion"].ToString()) : null; var isMinInclusive = token["IsMinInclusive"].ToObject<bool>(); var isMaxInclusive = token["IsMaxInclusive"].ToObject<bool>(); return new VersionRange(minVersion, isMinInclusive, maxVersion, isMaxInclusive, null, originalString); } catch (Exception ex) { throw new InvalidOperationException("反序列化VersionRange失败", ex); } } }
方案3:配置全局Json转换器
不想给每个类加特性的话,可以在JsonSerializerSettings中注册全局转换器,让所有VersionRange的反序列化操作都使用自定义转换器:
var settings = new JsonSerializerSettings(); settings.Converters.Add(new VersionRangeConverter()); // 替换原读取逻辑,显式使用自定义配置 DependencyDoc dependencyDoc = JsonConvert.DeserializeObject<DependencyDoc>(response.Resource.ToString(), settings);
内容的提问来源于stack exchange,提问作者S2K
相关产品推荐
相关产品推荐

