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

从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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 16:43:11