Skip to content

Commit

Permalink
Use local Newtonsoft settings when serializing/deserializing instead …
Browse files Browse the repository at this point in the history
…of assigning global settings (#3402)

By assigning global settings, this SDK would affect Json serialization done by users with Newtonsoft outside of this SDK. Now we use the same settings as before (to avoid regressing on #3903), but without assigning them globally.

Addresses #3358
  • Loading branch information
timtay-microsoft authored Dec 4, 2023
1 parent 005260a commit 12a737a
Show file tree
Hide file tree
Showing 43 changed files with 328 additions and 155 deletions.
4 changes: 2 additions & 2 deletions common/src/HttpMessageHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ internal static void SetHttpRequestMessageContent<T>(HttpRequestMessage requestM
#if NET451
requestMessage.Content = new ObjectContent<T>(entity, s_jsonFormatter);
#else
string str = JsonConvert.SerializeObject(entity);
string str = JsonConvert.SerializeObject(entity, JsonSerializerSettingsInitializer.GetJsonSerializerSettings());
requestMessage.Content = new StringContent(str, Encoding.UTF8, ApplicationJson);
#endif
}
}
}
}
4 changes: 2 additions & 2 deletions common/src/service/ExceptionHandlingHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,14 @@ public static async Task<ErrorCode> GetExceptionCodeAsync(HttpResponseMessage re
int errorCodeValue = (int)ErrorCode.InvalidErrorCode;
try
{
IoTHubExceptionResult responseContent = JsonConvert.DeserializeObject<IoTHubExceptionResult>(responseContentStr);
IoTHubExceptionResult responseContent = JsonConvert.DeserializeObject<IoTHubExceptionResult>(responseContentStr, JsonSerializerSettingsInitializer.GetJsonSerializerSettings());

try
{
var messageFields = new Dictionary<string, string>();
if (responseContent?.Message != null)
{
messageFields = JsonConvert.DeserializeObject<Dictionary<string, string>>(responseContent.Message);
messageFields = JsonConvert.DeserializeObject<Dictionary<string, string>>(responseContent.Message, JsonSerializerSettingsInitializer.GetJsonSerializerSettings());
}

if (messageFields != null
Expand Down
7 changes: 2 additions & 5 deletions common/src/service/HttpClientHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,6 @@ public HttpClientHelper(
_defaultErrorMapping = defaultErrorMapping;
_defaultOperationTimeout = timeout;

// Specify the JsonSerializerSettings. Check JsonSerializerSettingsInitializer for more details.
JsonConvert.DefaultSettings = JsonSerializerSettingsInitializer.GetJsonSerializerSettingsDelegate();

// We need two types of HttpClients, one with our default operation timeout, and one without. The one without will rely on
// a cancellation token.

Expand Down Expand Up @@ -312,7 +309,7 @@ private static async Task<T> ReadResponseMessageAsync<T>(HttpResponseMessage mes
}

string str = await message.Content.ReadHttpContentAsStringAsync(token).ConfigureAwait(false);
T entity = JsonConvert.DeserializeObject<T>(str);
T entity = JsonConvert.DeserializeObject<T>(str, JsonSerializerSettingsInitializer.GetJsonSerializerSettings());

// Etag in the header is considered authoritative
var eTagHolder = entity as IETagHolder;
Expand Down Expand Up @@ -575,7 +572,7 @@ private Task PostAsyncHelper<T1>(
}
else
{
string str = JsonConvert.SerializeObject(entity);
string str = JsonConvert.SerializeObject(entity, JsonSerializerSettingsInitializer.GetJsonSerializerSettings());
requestMsg.Content = new StringContent(str, System.Text.Encoding.UTF8, ApplicationJson);
}
}
Expand Down
36 changes: 36 additions & 0 deletions e2e/test/JsonSerializerSettingsInitializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using Newtonsoft.Json;

namespace Microsoft.Azure.Devices.E2ETests
{
/// <summary>
/// A class to initialize JsonSerializerSettings which can be applied to the project.
/// </summary>
internal static class JsonSerializerSettingsInitializer
{
/// <summary>
/// A static instance of JsonSerializerSettings which sets DateParseHandling to None.
/// </summary>
/// <remarks>
/// By default, serializing/deserializing with Newtonsoft.Json will try to parse date-formatted
/// strings to a date type, which drops trailing zeros in the microseconds date portion. By
/// specifying DateParseHandling with None, the original string will be read as-is. For more details
/// about the known issue, see https://github.com/JamesNK/Newtonsoft.Json/issues/1511.
/// </remarks>
private static readonly JsonSerializerSettings s_settings = new JsonSerializerSettings
{
DateParseHandling = DateParseHandling.None
};

/// <summary>
/// Returns JsonSerializerSettings Func delegate
/// </summary>
internal static JsonSerializerSettings GetJsonSerializerSettings()
{
return s_settings;
}
}
}
2 changes: 1 addition & 1 deletion e2e/test/helpers/ImportExportHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public static Stream BuildImportStream<T>(IReadOnlyList<T> items)

foreach (T item in items)
{
itemsFileSb.AppendLine(JsonConvert.SerializeObject(item));
itemsFileSb.AppendLine(JsonConvert.SerializeObject(item, JsonSerializerSettingsInitializer.GetJsonSerializerSettings()));
}

byte[] itemsFileInBytes = Encoding.Default.GetBytes(itemsFileSb.ToString());
Expand Down
56 changes: 28 additions & 28 deletions e2e/test/iothub/method/MethodE2ETests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,91 +29,91 @@ public class MethodE2ETests : E2EMsTestBase

private static readonly TimeSpan s_defaultMethodTimeoutMinutes = TimeSpan.FromMinutes(2);

[TestMethodWithRetry(Max=3)]
[TestMethodWithRetry(Max = 3)]
[Timeout(TestTimeoutMilliseconds)]
public async Task Method_DeviceReceivesMethodAndResponse_Mqtt()
{
await SendMethodAndRespondAsync(Client.TransportType.Mqtt_Tcp_Only, SetDeviceReceiveMethodAsync).ConfigureAwait(false);
}

[TestMethodWithRetry(Max=3)]
[TestMethodWithRetry(Max = 3)]
[Timeout(TestTimeoutMilliseconds)]
public async Task Method_DeviceReceivesMethodAndResponse_MqttWs()
{
await SendMethodAndRespondAsync(Client.TransportType.Mqtt_WebSocket_Only, SetDeviceReceiveMethodAsync).ConfigureAwait(false);
}

[TestMethodWithRetry(Max=3)]
[TestMethodWithRetry(Max = 3)]
[Timeout(TestTimeoutMilliseconds)]
public async Task Method_DeviceUnsubscribes_Mqtt()
{
await SendMethodAndUnsubscribeAsync(Client.TransportType.Mqtt_Tcp_Only, SubscribeAndUnsubscribeMethodAsync).ConfigureAwait(false);
}

[TestMethodWithRetry(Max=3)]
[TestMethodWithRetry(Max = 3)]
[Timeout(TestTimeoutMilliseconds)]
public async Task Method_DeviceUnsubscribes_MqttWs()
{
await SendMethodAndUnsubscribeAsync(Client.TransportType.Mqtt_WebSocket_Only, SubscribeAndUnsubscribeMethodAsync).ConfigureAwait(false);
}

[TestMethodWithRetry(Max=3)]
[TestMethodWithRetry(Max = 3)]
[Timeout(TestTimeoutMilliseconds)]
public async Task Method_DeviceReceivesMethodAndResponseWithDefaultMethodHandler_Mqtt()
{
await SendMethodAndRespondAsync(Client.TransportType.Mqtt_Tcp_Only, SetDeviceReceiveMethodDefaultHandlerAsync).ConfigureAwait(false);
}

[TestMethodWithRetry(Max=3)]
[TestMethodWithRetry(Max = 3)]
[Timeout(TestTimeoutMilliseconds)]
public async Task Method_DeviceReceivesMethodAndResponseWithDefaultMethodHandler_MqttWs()
{
await SendMethodAndRespondAsync(Client.TransportType.Mqtt_WebSocket_Only, SetDeviceReceiveMethodDefaultHandlerAsync).ConfigureAwait(false);
}

[TestMethodWithRetry(Max=3)]
[TestMethodWithRetry(Max = 3)]
[Timeout(TestTimeoutMilliseconds)]
public async Task Method_DeviceReceivesMethodAndResponse_Amqp()
{
await SendMethodAndRespondAsync(Client.TransportType.Amqp_Tcp_Only, SetDeviceReceiveMethodAsync).ConfigureAwait(false);
}

[TestMethodWithRetry(Max=3)]
[TestMethodWithRetry(Max = 3)]
[Timeout(TestTimeoutMilliseconds)]
public async Task Method_DeviceReceivesMethodAndResponse_AmqpWs()
{
await SendMethodAndRespondAsync(Client.TransportType.Amqp_WebSocket_Only, SetDeviceReceiveMethodAsync).ConfigureAwait(false);
}

[TestMethodWithRetry(Max=3)]
[TestMethodWithRetry(Max = 3)]
[Timeout(TestTimeoutMilliseconds)]
public async Task Method_DeviceUnsubscribes_Amqp()
{
await SendMethodAndUnsubscribeAsync(Client.TransportType.Amqp_Tcp_Only, SubscribeAndUnsubscribeMethodAsync).ConfigureAwait(false);
}

[TestMethodWithRetry(Max=3)]
[TestMethodWithRetry(Max = 3)]
[Timeout(TestTimeoutMilliseconds)]
public async Task Method_DeviceUnsubscribes_AmqpWs()
{
await SendMethodAndUnsubscribeAsync(Client.TransportType.Amqp_WebSocket_Only, SubscribeAndUnsubscribeMethodAsync).ConfigureAwait(false);
}

[TestMethodWithRetry(Max=3)]
[TestMethodWithRetry(Max = 3)]
[Timeout(TestTimeoutMilliseconds)]
public async Task Method_DeviceReceivesMethodAndResponseWithDefaultMethodHandler_Amqp()
{
await SendMethodAndRespondAsync(Client.TransportType.Amqp_Tcp_Only, SetDeviceReceiveMethodDefaultHandlerAsync).ConfigureAwait(false);
}

[TestMethodWithRetry(Max=3)]
[TestMethodWithRetry(Max = 3)]
[Timeout(TestTimeoutMilliseconds)]
public async Task Method_DeviceReceivesMethodAndResponseWithDefaultMethodHandler_AmqpWs()
{
await SendMethodAndRespondAsync(Client.TransportType.Amqp_WebSocket_Only, SetDeviceReceiveMethodDefaultHandlerAsync).ConfigureAwait(false);
}

[TestMethodWithRetry(Max=3)]
[TestMethodWithRetry(Max = 3)]
[Timeout(TestTimeoutMilliseconds)]
public async Task Method_ServiceSendsMethodThroughProxyWithDefaultTimeout()
{
Expand All @@ -129,7 +129,7 @@ await SendMethodAndRespondAsync(
.ConfigureAwait(false);
}

[TestMethodWithRetry(Max=3)]
[TestMethodWithRetry(Max = 3)]
[Timeout(TestTimeoutMilliseconds)]
public async Task Method_ServiceSendsMethodThroughProxyWithCustomTimeout()
{
Expand All @@ -146,7 +146,7 @@ await SendMethodAndRespondAsync(
.ConfigureAwait(false);
}

[TestMethodWithRetry(Max=3)]
[TestMethodWithRetry(Max = 3)]
[Timeout(TestTimeoutMilliseconds)]
public async Task Method_ServiceInvokeDeviceMethodWithUnknownDeviceThrows()
{
Expand All @@ -172,63 +172,63 @@ public async Task Method_ServiceInvokeDeviceMethodWithUnknownDeviceThrows()
await serviceClient.CloseAsync().ConfigureAwait(false);
}

[TestMethodWithRetry(Max=3)]
[TestMethodWithRetry(Max = 3)]
[Timeout(TestTimeoutMilliseconds)]
public async Task Method_ModuleReceivesMethodAndResponse_Mqtt()
{
await SendMethodAndRespondAsync(Client.TransportType.Mqtt_Tcp_Only, SetModuleReceiveMethodAsync).ConfigureAwait(false);
}

[TestMethodWithRetry(Max=3)]
[TestMethodWithRetry(Max = 3)]
[Timeout(TestTimeoutMilliseconds)]
public async Task Method_ModuleReceivesMethodAndResponse_MqttWs()
{
await SendMethodAndRespondAsync(Client.TransportType.Mqtt_WebSocket_Only, SetModuleReceiveMethodAsync).ConfigureAwait(false);
}

[TestMethodWithRetry(Max=3)]
[TestMethodWithRetry(Max = 3)]
[Timeout(TestTimeoutMilliseconds)]
public async Task Method_ModuleReceivesMethodAndResponseWithDefaultMethodHandler_Mqtt()
{
await SendMethodAndRespondAsync(Client.TransportType.Mqtt_Tcp_Only, SetModuleReceiveMethodDefaultHandlerAsync).ConfigureAwait(false);
}

[TestMethodWithRetry(Max=3)]
[TestMethodWithRetry(Max = 3)]
[Timeout(TestTimeoutMilliseconds)]
public async Task Method_ModuleReceivesMethodAndResponseWithDefaultMethodHandler_MqttWs()
{
await SendMethodAndRespondAsync(Client.TransportType.Mqtt_WebSocket_Only, SetModuleReceiveMethodDefaultHandlerAsync).ConfigureAwait(false);
}

[TestMethodWithRetry(Max=3)]
[TestMethodWithRetry(Max = 3)]
[Timeout(TestTimeoutMilliseconds)]
public async Task Method_ModuleReceivesMethodAndResponse_Amqp()
{
await SendMethodAndRespondAsync(Client.TransportType.Amqp_Tcp_Only, SetModuleReceiveMethodAsync).ConfigureAwait(false);
}

[TestMethodWithRetry(Max=3)]
[TestMethodWithRetry(Max = 3)]
[Timeout(TestTimeoutMilliseconds)]
public async Task Method_ModuleReceivesMethodAndResponse_AmqpWs()
{
await SendMethodAndRespondAsync(Client.TransportType.Amqp_WebSocket_Only, SetModuleReceiveMethodAsync).ConfigureAwait(false);
}

[TestMethodWithRetry(Max=3)]
[TestMethodWithRetry(Max = 3)]
[Timeout(TestTimeoutMilliseconds)]
public async Task Method_ModuleReceivesMethodAndResponseWithDefaultMethodHandler_Amqp()
{
await SendMethodAndRespondAsync(Client.TransportType.Amqp_Tcp_Only, SetModuleReceiveMethodDefaultHandlerAsync).ConfigureAwait(false);
}

[TestMethodWithRetry(Max=3)]
[TestMethodWithRetry(Max = 3)]
[Timeout(TestTimeoutMilliseconds)]
public async Task Method_ModuleReceivesMethodAndResponseWithDefaultMethodHandler_AmqpWs()
{
await SendMethodAndRespondAsync(Client.TransportType.Amqp_WebSocket_Only, SetModuleReceiveMethodDefaultHandlerAsync).ConfigureAwait(false);
}

[TestMethodWithRetry(Max=3)]
[TestMethodWithRetry(Max = 3)]
[Timeout(TestTimeoutMilliseconds)]
public async Task Method_ServiceInvokeDeviceMethodWithUnknownModuleThrows()
{
Expand Down Expand Up @@ -256,7 +256,7 @@ public async Task Method_ServiceInvokeDeviceMethodWithUnknownModuleThrows()
await serviceClient.CloseAsync().ConfigureAwait(false);
}

[TestMethodWithRetry(Max=3)]
[TestMethodWithRetry(Max = 3)]
[Timeout(TestTimeoutMilliseconds)]
public async Task Method_ServiceInvokeDeviceMethodWithNullPayload_DoesNotThrow()
{
Expand Down Expand Up @@ -302,15 +302,15 @@ await deviceClient
}
}

[TestMethodWithRetry(Max=3)]
[TestMethodWithRetry(Max = 3)]
[Timeout(TestTimeoutMilliseconds)]
public async Task Method_ServiceInvokeDeviceMethodWithDateTimePayload_DoesNotThrow()
{
// arrange

var date = new DateTimeOffset(638107582284599400, TimeSpan.FromHours(1));

string responseJson = JsonConvert.SerializeObject(new TestDateTime { Iso8601String = date.ToString("o", CultureInfo.InvariantCulture) });
string responseJson = JsonConvert.SerializeObject(new TestDateTime { Iso8601String = date.ToString("o", CultureInfo.InvariantCulture) }, JsonSerializerSettingsInitializer.GetJsonSerializerSettings());
byte[] responseBytes = Encoding.UTF8.GetBytes(responseJson);

const string commandName = "GetDateTime";
Expand Down Expand Up @@ -340,7 +340,7 @@ await deviceClient

CloudToDeviceMethodResult result = await serviceClient.InvokeDeviceMethodAsync(testDevice.Id, c2dMethod).ConfigureAwait(false);
string actualResultJson = result.GetPayloadAsJson();
TestDateTime myDtoValue = JsonConvert.DeserializeObject<TestDateTime>(actualResultJson);
TestDateTime myDtoValue = JsonConvert.DeserializeObject<TestDateTime>(actualResultJson, JsonSerializerSettingsInitializer.GetJsonSerializerSettings());
string value = myDtoValue.Iso8601String;

Action act = () => DateTimeOffset.ParseExact(value, "o", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind);
Expand Down
Loading

0 comments on commit 12a737a

Please sign in to comment.