GetTwinAsync() 抛出序列化转换异常 -> DatetimeOffset

GetTwinAsync() throws serialization casting exception -> DatetimeOffset

您好,我们在尝试从 IOT 集线器获取设备孪生时遇到此转换错误

Microsoft.Azure.Devices.Common.Exceptions.IotHubException: Unable to cast object of type 'System.DateTimeOffset' to type 'System.DateTime'. ---> System.InvalidCastException: Unable to cast object of type 'System.DateTimeOffset' to type 'System.DateTime'. at Microsoft.Azure.Devices.Shared.TwinJsonConverter.ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.DeserializeConvertable(JsonConverter converter, JsonReader reader, Type objectType, 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.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings) at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings) at Microsoft.Azure.Devices.HttpClientHelper.<ReadResponseMessageAsync>d__17`1.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Azure.Devices.HttpClientHelper.<>c__DisplayClass10_0`1.<<GetAsync>b__9>d.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Azure.Devices.HttpClientHelper.<ExecuteAsync>d__35.MoveNext() --- End of inner exception stack trace --- at Microsoft.Azure.Devices.HttpClientHelper.<ExecuteAsync>d__35.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Azure.Devices.HttpClientHelper.<GetAsync>d__10`1.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)

并且我们使用设备SDK, _registryManager = RegistryManager.CreateFromConnectionString(connectionString); await _registryManager.GetTwinAsync(deviceId); 到目前为止,我无法从本地重现这一点,但它正在我们的一个测试环境中发生。

尽管 DateTimeOffset 结构提供比 DateTime 结构更大程度的时区意识,但 DateTime 参数在方法调用中使用得更普遍。因此,将 DateTimeOffset 值转换为 DateTime 值的能力尤为重要,反之亦然。更多信息 here.

我认为您需要检查不同环境下的时区设置和设备孪生的 属性 值。

使用来自 registryManager 的 Rest API 获取设备孪生:

        #region get the device twin

        var finfo = registryManager.GetType().GetField("httpClientHelper", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(registryManager);
        HttpClient client = finfo.GetType().GetField("httpClientObj", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(finfo) as HttpClient;
        var provider = finfo.GetType().GetField("authenticationHeaderProvider", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(finfo);
        dynamic sastoken = provider.GetType().InvokeMember("GetAuthorizationHeader", BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null, provider, null);
        if (client.DefaultRequestHeaders.Authorization == null)
            client.DefaultRequestHeaders.Add("Authorization", sastoken);

        var jsontext = client.GetStringAsync("https://{yourNamespace}.azure-devices.net/twins/{yourDeviceId}?api-version=2018-04-01").Result;

        // log this jsontext

        var twin = JsonConvert.DeserializeObject<Twin>(jsontext);

        #endregion

在您的 "bad" 环境中使用上面的代码片段来查看适合您设备的 json 格式的文本。

我们花了一段时间才弄清楚,但请检查默认的 json 序列化程序是否已将 DateParseHandling 配置为 DateTimeOffset。

      JsonConvert.DefaultSettings = () => new JsonSerializerSettings
        {
            DateParseHandling = DateParseHandling.DateTimeOffset
        };

如果是这种情况,您将必须覆盖 DeviceTwin 的序列化程序,因为其他代码必须依赖于此设置。