Cosmos DB 序列化问题引发错误,将 Int 读取为 Double

Posted

技术标签:

【中文标题】Cosmos DB 序列化问题引发错误,将 Int 读取为 Double【英文标题】:Cosmos DB Serialization Issue Throws Error Reading An Int as Double 【发布时间】:2021-05-02 08:41:58 【问题描述】:

在我的一些文档中,我的 .NET Cosmos Db 客户端 (v3) 不会读取文档。该文档有一个属性 DistanceInMetres,并在我的类中设置为一个 int。

   public int DistanceInMetres  get; set; 

当我检查导致此错误的文档之一(有很多)时,属性设置正确。

   "distanceInMetres": 3272,

我收到的错误是:

 Input string '3272.0' is not a valid integer. Path 'distanceInMetres', line 1, position 1586.

这是执行请求的代码:

    public async Task<T> ReadEntityAsync<T>(string id, string pKey)
    
       var doc= await container.ReadItemAsync<T>(id, new PartitionKey(pKey));
       return doc;
    

序列化错误不依赖于客户端查询,它发生在 Container.Read 以及针对多个文档的 Container.Query 上。我尝试将底层模型属性从 int 更改为 double 我仍然得到同样的错误!我已经检查了具有相同模型的其他文档,这些文档返回时没有错误,看看是否有任何差异但没有(我可以看到)。如果我进入门户并手动更改属性,同时保持值相同并更新文档,则会出现错误。 我不认为这是通过单个和多个查询发生的数据访问问题。 这是 CosmosDB 上的错误吗?有人有什么想法吗?

作为更新: 当我使用 DocumentClient 访问数据库时:

 DocumentClient documentClient = new DocumentClient(new 
 Uri("url"),"Key",                                                         
 serializerSettings: new JsonSerializerSettings  ContractResolver = new 
 CamelCasePropertyNamesContractResolver() ) ;

文档被毫无例外地读取。

当我使用 CosmosClient 时:

   CosmosClient cosmo = new 
   CosmosClient(config["keys:cosmosClientConStr"], new CosmosClientOptions
   
       ConnectionMode = ConnectionMode.Direct,
       SerializerOptions = new CosmosSerializationOptions  
       PropertyNamingPolicy = CosmosPropertyNamingPolicy.CamelCase 
        );

        Container container = cosmo.GetContainer(config["keys:dbName"], 
 config["keys:colName"]);

我在某些文档上遇到了异常。奇怪的是,如果我进入门户并重新保存文档,一切正常。 更新:如果我将类类型从 T 更改为 Dynamic 我可以读回该项目 即使它存储为整数,它也确实被读取为小数。 这是堆栈跟踪。

   at Newtonsoft.Json.JsonTextReader.ParseReadNumber(ReadType readType, Char firstChar, Int32 initialPosition)
   at Newtonsoft.Json.JsonTextReader.ParseNumber(ReadType readType)
   at Newtonsoft.Json.JsonTextReader.ReadNumberValue(ReadType readType)
   at Newtonsoft.Json.JsonTextReader.ReadAsInt32()
   at Newtonsoft.Json.JsonReader.ReadForType(JsonContract contract, Boolean hasConverter)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
   at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
   at Newtonsoft.Json.JsonSerializer.Deserialize(JsonReader reader, Type objectType)
   at Newtonsoft.Json.JsonSerializer.Deserialize[T](JsonReader reader)
   at Microsoft.Azure.Cosmos.CosmosJsonDotNetSerializer.FromStream[T](Stream stream)
   at Microsoft.Azure.Cosmos.CosmosJsonSerializerWrapper.FromStream[T](Stream stream)
   at Microsoft.Azure.Cosmos.CosmosJsonSerializerWrapper.FromStream[T](Stream stream)
   at Microsoft.Azure.Cosmos.CosmosSerializerCore.FromStream[T](Stream stream)
   at Microsoft.Azure.Cosmos.CosmosResponseFactoryCore.ToObjectpublic[T](ResponseMessage responseMessage)
   at Microsoft.Azure.Cosmos.CosmosResponseFactoryCore.<CreateItemResponse>b__8_0[T](ResponseMessage cosmosResponseMessage)
   at Microsoft.Azure.Cosmos.CosmosResponseFactoryCore.ProcessMessage[T](ResponseMessage responseMessage, Func`2 createResponse)
   at Microsoft.Azure.Cosmos.CosmosResponseFactoryCore.CreateItemResponse[T](ResponseMessage responseMessage)
   at Microsoft.Azure.Cosmos.ContainerCore.<ReadItemAsync>d__56`1.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()
   at Microsoft.Azure.Cosmos.ClientContextCore.<RunWithDiagnosticsHelperAsync>d__38`1.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at CT5ApiService.ApiData.CosmosStore.<ReadEntityAsync>d__4`1.MoveNext() in C:\Users\pjsta\source\repos\CT_CLOUD\CT5ApiService\ApiData\CosmosStore.cs:line 81
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at CT5ApiService.Api.JobsApi.<Get>d__3.MoveNext() in C:\Users\pjsta\source\repos\CT_CLOUD\CT5ApiService\Api\JobsApi.cs:line 49
       ````

【问题讨论】:

所有文档的创建方式都相同吗?还是问题仅限于特定用户/进程/客户创建的文档? 该属性在数据库中不是小数,并且存储为整数,这似乎很奇怪。 JsonConvert 通话是否使用任何文化? 请显示产生错误的相关代码。 不,我没有使用 db 在英国南部的任何文化,我也是。 【参考方案1】:

鉴于您的评论:

如果我进入门户并重新保存文档,一切正常

这表明该项目的最后修改可能会引入一些不正确的编码,这些编码不是真正的 UTF-8 整数值。您的重新保存更正了编码。隐藏字符会导致serialization problems。

更强大的方法是使用ReadItemStreamAsync SDK 方法读取原始Stream,然后在流上使用System.Text.Json.JsonSerializer.DeserializeAsyncJsonConvert 转换为项目。然后您就有机会检查原始值并处理出现的任何异常。

samples repo 中显示了一个示例:

using (ResponseMessage responseMessage = await container.ReadItemStreamAsync(
    partitionKey: new PartitionKey("Account1"),
    id: "SalesOrder1"))

    // Item stream operations do not throw exceptions for better performance
    if (responseMessage.IsSuccessStatusCode)
    
        SalesOrder streamResponse = FromStream<SalesOrder>(responseMessage.Content);
        Console.WriteLine($"\n1.2.2 - Item Read streamResponse.Id");

        // Log the diagnostics
        Console.WriteLine($"\n1.2.2 - Item Read Diagnostics: responseMessage.Diagnostics.ToString()");
    
    else
    
        Console.WriteLine($"Read item from stream failed. Status code: responseMessage.StatusCode Message: responseMessage.ErrorMessage");
    

【讨论】:

不幸的是,这一行抛出了同样的错误。 - .return Serializer.Deserialize(jsonTextReader);但是,我认为您的权利在保存文档时有问题。使用 DocumentClient 不会引发异常,因此我将不得不用它来实现我的类。

以上是关于Cosmos DB 序列化问题引发错误,将 Int 读取为 Double的主要内容,如果未能解决你的问题,请参考以下文章

将Cosmos DB模拟器(MongoDB API)迁移到Azure时发生致命错误

带有 Cosmos DB 绑定的 Azure 函数返回格式化的 DateTime?

Cosmos db Rest API - 错误 401 未经授权

在 cosmos db 和 asp.net core 中处理请求时发生未处理的异常

Azure Cosmos DB - '请求率很大。删除项目时可能需要更多请求单位的错误

通过 MongoAPI 对 Azure Cosmos DB 进行聚合查询时出现意外错误