Skip to content

Commit ad522d6

Browse files
authored
(#115) Specify culture for JSON serialization. (#117)
1 parent 9780f81 commit ad522d6

File tree

8 files changed

+122
-43
lines changed

8 files changed

+122
-43
lines changed

src/CommunityToolkit.Datasync.Server.Abstractions/Json/DateTimeConverter.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
using System.Globalization;
56
using System.Text.Json;
67
using System.Text.Json.Serialization;
78

@@ -14,12 +15,13 @@ namespace CommunityToolkit.Datasync.Server.Abstractions.Json;
1415
public class DateTimeConverter : JsonConverter<DateTime>
1516
{
1617
private const string format = "yyyy-MM-dd'T'HH:mm:ss.fffK";
18+
private static readonly CultureInfo culture = new("en-US");
1719

1820
/// <inheritdoc />
1921
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
2022
=> DateTime.Parse(reader.GetString() ?? string.Empty);
2123

2224
/// <inheritdoc />
2325
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
24-
=> writer.WriteStringValue(value.ToUniversalTime().ToString(format));
26+
=> writer.WriteStringValue(value.ToUniversalTime().ToString(format, culture));
2527
}

src/CommunityToolkit.Datasync.Server.Abstractions/Json/DateTimeOffsetConverter.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
using System.Globalization;
56
using System.Text.Json;
67
using System.Text.Json.Serialization;
78

@@ -14,12 +15,13 @@ namespace CommunityToolkit.Datasync.Server.Abstractions.Json;
1415
public class DateTimeOffsetConverter : JsonConverter<DateTimeOffset>
1516
{
1617
private const string format = "yyyy-MM-dd'T'HH:mm:ss.fffK";
18+
private static readonly CultureInfo culture = new("en-US");
1719

1820
/// <inheritdoc />
1921
public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
2022
=> DateTimeOffset.Parse(reader.GetString() ?? string.Empty);
2123

2224
/// <inheritdoc />
2325
public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options)
24-
=> writer.WriteStringValue(value.ToUniversalTime().UtcDateTime.ToString(format));
26+
=> writer.WriteStringValue(value.ToUniversalTime().UtcDateTime.ToString(format, culture));
2527
}

src/CommunityToolkit.Datasync.Server.Abstractions/Json/TimeOnlyConverter.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
using System.Globalization;
56
using System.Text.Json;
67
using System.Text.Json.Serialization;
78

@@ -14,12 +15,13 @@ namespace CommunityToolkit.Datasync.Server.Abstractions.Json;
1415
public class TimeOnlyConverter : JsonConverter<TimeOnly>
1516
{
1617
private const string format = "HH:mm:ss.fff";
18+
private static readonly CultureInfo culture = new("en-US");
1719

1820
/// <inheritdoc />
1921
public override TimeOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
2022
=> TimeOnly.Parse(reader.GetString() ?? string.Empty);
2123

2224
/// <inheritdoc />
2325
public override void Write(Utf8JsonWriter writer, TimeOnly value, JsonSerializerOptions options)
24-
=> writer.WriteStringValue(value.ToString(format));
26+
=> writer.WriteStringValue(value.ToString(format, culture));
2527
}

tests/CommunityToolkit.Datasync.Client.Test/Authentication/GenericAuthenticationProvider_Tests.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ public class GenericAuthenticationProvider_Tests
3737
};
3838
#endregion
3939

40+
[Fact]
41+
public void Ctor_WhiteSpace_Header_Throws()
42+
{
43+
Action act = () => _ = new GenericAuthenticationProvider(_ => Task.FromResult(ValidAuthenticationToken), "X-ZUMO-AUTH", " ");
44+
act.Should().Throw<ArgumentException>();
45+
}
46+
4047
[Fact]
4148
public void Ctor_NullTokenRequestor_Throws()
4249
{

tests/CommunityToolkit.Datasync.Server.Abstractions.Test/Json/DateTimeConverter_Tests.cs

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,32 +9,48 @@ namespace CommunityToolkit.Datasync.Server.Abstractions.Test.Json;
99
[ExcludeFromCodeCoverage]
1010
public class DateTimeConverter_Tests : SerializerTests
1111
{
12-
[Fact]
13-
public void Converter_ReadsJson()
12+
[Theory]
13+
[MemberData(nameof(Locales))]
14+
public void Converter_ReadsJson(string culture)
1415
{
1516
string json = """{"updatedAt":"2021-08-21T12:30:15.123+00:00"}""";
16-
DateTime value = DateTime.Parse("2021-08-21T12:30:15.123+00:00");
17+
DateTime value = new(2021, 8, 21, 12, 30, 15, 123, DateTimeKind.Utc);
1718

18-
Entity entity = JsonSerializer.Deserialize<Entity>(json, SerializerOptions);
19-
entity.UpdatedAt.ToFileTime().Should().Be(value.ToFileTime());
19+
TestWithCulture(culture, () =>
20+
{
21+
Entity entity = JsonSerializer.Deserialize<Entity>(json, SerializerOptions);
22+
entity.UpdatedAt.ToFileTime().Should().Be(value.ToFileTime());
23+
});
2024
}
2125

22-
[Fact]
23-
public void Converter_WritesJson()
26+
[Theory]
27+
[MemberData(nameof(Locales))]
28+
public void Converter_WritesJson(string culture)
2429
{
2530
string json = """{"updatedAt":"2021-08-21T12:30:15.123Z"}""";
26-
Entity entity = new() { UpdatedAt = DateTime.Parse("2021-08-21T12:30:15.1234567+00:00") };
27-
string actual = JsonSerializer.Serialize(entity, SerializerOptions);
28-
Assert.Equal(json, actual);
31+
DateTime value = new(2021, 8, 21, 12, 30, 15, 123, 456, DateTimeKind.Utc);
32+
33+
TestWithCulture(culture, () =>
34+
{
35+
Entity entity = new() { UpdatedAt = value };
36+
string actual = JsonSerializer.Serialize(entity, SerializerOptions);
37+
Assert.Equal(json, actual);
38+
});
2939
}
3040

31-
[Fact]
32-
public void Converter_WritesJson_WithTimeZone()
41+
[Theory]
42+
[MemberData(nameof(Locales))]
43+
public void Converter_WritesJson_WithTimeZone(string culture)
3344
{
3445
string json = """{"updatedAt":"2021-08-21T12:30:15.123Z"}""";
35-
Entity entity = new() { UpdatedAt = DateTime.Parse("2021-08-21T20:30:15.1234567+08:00") };
36-
string actual = JsonSerializer.Serialize(entity, SerializerOptions);
37-
Assert.Equal(json, actual);
46+
DateTime value = DateTime.Parse("2021-08-21T20:30:15.1234567+08:00");
47+
48+
TestWithCulture(culture, () =>
49+
{
50+
Entity entity = new() { UpdatedAt = value };
51+
string actual = JsonSerializer.Serialize(entity, SerializerOptions);
52+
Assert.Equal(json, actual);
53+
});
3854
}
3955

4056
[Fact]

tests/CommunityToolkit.Datasync.Server.Abstractions.Test/Json/DateTimeOffsetConverter_Tests.cs

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,32 +9,48 @@ namespace CommunityToolkit.Datasync.Server.Abstractions.Test.Json;
99
[ExcludeFromCodeCoverage]
1010
public class DateTimeOffsetConverter_Tests : SerializerTests
1111
{
12-
[Fact]
13-
public void Converter_ReadsJson()
12+
[Theory]
13+
[MemberData(nameof(Locales))]
14+
public void Converter_ReadsJson(string culture)
1415
{
1516
string json = "{\"updatedAt\":\"2021-08-21T12:30:15.123+00:00\"}";
16-
DateTimeOffset value = DateTimeOffset.Parse("2021-08-21T12:30:15.123+00:00");
17+
DateTimeOffset value = new(2021, 8, 21, 12, 30, 15, 123, TimeSpan.Zero);
1718

18-
Entity entity = JsonSerializer.Deserialize<Entity>(json, SerializerOptions);
19-
entity.UpdatedAt.ToFileTime().Should().Be(value.ToFileTime());
19+
TestWithCulture(culture, () =>
20+
{
21+
Entity entity = JsonSerializer.Deserialize<Entity>(json, SerializerOptions);
22+
entity.UpdatedAt.ToFileTime().Should().Be(value.ToFileTime());
23+
});
2024
}
2125

22-
[Fact]
23-
public void Converter_WritesJson()
26+
[Theory]
27+
[MemberData(nameof(Locales))]
28+
public void Converter_WritesJson(string culture)
2429
{
2530
string json = "{\"updatedAt\":\"2021-08-21T12:30:15.123Z\"}";
26-
Entity entity = new() { UpdatedAt = DateTimeOffset.Parse("2021-08-21T12:30:15.1234567+00:00") };
27-
string actual = JsonSerializer.Serialize(entity, SerializerOptions);
28-
Assert.Equal(json, actual);
31+
DateTimeOffset value = new(2021, 8, 21, 12, 30, 15, 123, 456, TimeSpan.Zero);
32+
33+
TestWithCulture(culture, () =>
34+
{
35+
Entity entity = new() { UpdatedAt = value };
36+
string actual = JsonSerializer.Serialize(entity, SerializerOptions);
37+
Assert.Equal(json, actual);
38+
});
2939
}
3040

31-
[Fact]
32-
public void Converter_WritesJson_WithTimeZone()
41+
[Theory]
42+
[MemberData(nameof(Locales))]
43+
public void Converter_WritesJson_WithTimeZone(string culture)
3344
{
3445
string json = "{\"updatedAt\":\"2021-08-21T12:30:15.123Z\"}";
35-
Entity entity = new() { UpdatedAt = DateTimeOffset.Parse("2021-08-21T20:30:15.1234567+08:00") };
36-
string actual = JsonSerializer.Serialize(entity, SerializerOptions);
37-
Assert.Equal(json, actual);
46+
DateTimeOffset value = new(2021, 8, 21, 20, 30, 15, 123, 456, TimeSpan.FromHours(8));
47+
48+
TestWithCulture(culture, () =>
49+
{
50+
Entity entity = new() { UpdatedAt = value };
51+
string actual = JsonSerializer.Serialize(entity, SerializerOptions);
52+
Assert.Equal(json, actual);
53+
});
3854
}
3955

4056
[Fact]

tests/CommunityToolkit.Datasync.Server.Abstractions.Test/Json/SerializerTests.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44

55
using Azure.Core.Serialization;
66
using CommunityToolkit.Datasync.Server.Abstractions.Json;
7+
using System.Globalization;
78
using System.Text.Json;
89
using System.Text.Json.Serialization;
910

11+
#pragma warning disable IDE0028 // Simplify collection initialization
12+
1013
namespace CommunityToolkit.Datasync.Server.Abstractions.Test.Json;
1114

1215
[ExcludeFromCodeCoverage]
@@ -37,4 +40,25 @@ public abstract class SerializerTests
3740
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
3841
ReadCommentHandling = JsonCommentHandling.Skip
3942
};
43+
44+
public static TheoryData<string> Locales => new()
45+
{
46+
"fr-FR",
47+
"da-DA",
48+
"en-US"
49+
};
50+
51+
protected static void TestWithCulture(string culture, Action act)
52+
{
53+
CultureInfo currentCulture = Thread.CurrentThread.CurrentCulture;
54+
Thread.CurrentThread.CurrentCulture = new CultureInfo(culture);
55+
try
56+
{
57+
act.Invoke();
58+
}
59+
finally
60+
{
61+
Thread.CurrentThread.CurrentCulture = currentCulture;
62+
}
63+
}
4064
}

tests/CommunityToolkit.Datasync.Server.Abstractions.Test/Json/TimeOnlyConverter_Tests.cs

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,33 @@ namespace CommunityToolkit.Datasync.Server.Abstractions.Test.Json;
99
[ExcludeFromCodeCoverage]
1010
public class TimeOnlyConverter_Tests : SerializerTests
1111
{
12-
[Fact]
13-
public void Converter_ReadsJson()
12+
[Theory]
13+
[MemberData(nameof(Locales))]
14+
public void Converter_ReadsJson(string culture)
1415
{
1516
string json = """{"updatedAt":"12:30:15.123"}""";
16-
TimeOnly value = TimeOnly.Parse("12:30:15.123");
17+
TimeOnly value = new(12, 30, 15, 123);
1718

18-
Entity entity = JsonSerializer.Deserialize<Entity>(json, SerializerOptions);
19-
entity.UpdatedAt.Ticks.Should().Be(value.Ticks);
19+
TestWithCulture(culture, () =>
20+
{
21+
Entity entity = JsonSerializer.Deserialize<Entity>(json, SerializerOptions);
22+
entity.UpdatedAt.Ticks.Should().Be(value.Ticks);
23+
});
2024
}
2125

22-
[Fact]
23-
public void Converter_WritesJson()
26+
[Theory]
27+
[MemberData(nameof(Locales))]
28+
public void Converter_WritesJson(string culture)
2429
{
2530
string json = """{"updatedAt":"12:30:15.123"}""";
26-
Entity entity = new() { UpdatedAt = TimeOnly.Parse("12:30:15.1234567") };
27-
string actual = JsonSerializer.Serialize(entity, SerializerOptions);
28-
Assert.Equal(json, actual);
31+
TimeOnly value = new(12, 30, 15, 123, 456);
32+
33+
TestWithCulture(culture, () =>
34+
{
35+
Entity entity = new() { UpdatedAt = value };
36+
string actual = JsonSerializer.Serialize(entity, SerializerOptions);
37+
Assert.Equal(json, actual);
38+
});
2939
}
3040

3141
[Fact]

0 commit comments

Comments
 (0)