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 的序列化程序,因为其他代码必须依赖于此设置。
您好,我们在尝试从 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 的序列化程序,因为其他代码必须依赖于此设置。