Skip to content

Commit

Permalink
Use invariant culture for (de)serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
MartinM85 committed Jul 18, 2024
1 parent e29f522 commit 9ccd477
Show file tree
Hide file tree
Showing 7 changed files with 311 additions and 12 deletions.
3 changes: 3 additions & 0 deletions src/serialization/form/FormSerializationWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ private void WriteAnyValue(string? key, object value)
case IEnumerable<object> coll:
WriteCollectionOfPrimitiveValues(key, coll);
break;
case byte[] coll:
WriteByteArrayValue(key, coll);
break;
case IParsable:
throw new InvalidOperationException("Form serialization does not support nested objects.");
default:
Expand Down
22 changes: 11 additions & 11 deletions src/serialization/json/JsonParseNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,14 +177,14 @@ public JsonParseNode(JsonElement node, KiotaJsonSerializationContext jsonSeriali
/// <returns>A <see cref="Date"/> value</returns>
public Date? GetDateValue()
{
var dateString = _jsonNode.GetString();
if(string.IsNullOrEmpty(dateString))
if(_jsonNode.ValueKind != JsonValueKind.String)
return null;

if(DateTime.TryParse(dateString, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var result))
return new Date(result);

return _jsonNode.Deserialize(_jsonSerializerContext.Date);
if(TryGetUsingTypeInfo(_jsonNode, _jsonSerializerContext.Date, out var date))
return date;
else if(DateTime.TryParse(_jsonNode.GetString(), CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var dt))
return new Date(dt);
else return null;
}

/// <summary>
Expand All @@ -193,14 +193,14 @@ public JsonParseNode(JsonElement node, KiotaJsonSerializationContext jsonSeriali
/// <returns>A <see cref="Time"/> value</returns>
public Time? GetTimeValue()
{
var dateString = _jsonNode.GetString();
if(string.IsNullOrEmpty(dateString))
if(_jsonNode.ValueKind != JsonValueKind.String)
return null;

if(DateTime.TryParse(dateString, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var result))
if(TryGetUsingTypeInfo(_jsonNode, _jsonSerializerContext.Time, out var time))
return time;
if(DateTime.TryParse(_jsonNode.GetString(), CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var result))
return new Time(result);

return _jsonNode.Deserialize(_jsonSerializerContext.Time);
else return null;
}

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion tests/serialization/form/FormSerializationWriterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ public void WriteAdditionalData_AreWrittenCorrectly()
var serializedString = reader.ReadToEnd();

// Assert
Assert.Equal("prop1=value1&prop2=2&prop3=true&prop4=2.25&prop5=3.14&prop6=4&prop7=5&prop8=2024-11-30T15%3A35%3A45.9870000%2B03%3A00&prop9=2024-11-30&prop10=23%3A46%3A59&prop11=P756DT4H6M8.01S&prop12=System.Byte%5B%5D&prop13=3adeb301-58f1-45c5-b820-ae5f4af13c89&prop14=127", serializedString);
Assert.Equal("prop1=value1&prop2=2&prop3=true&prop4=2.25&prop5=3.14&prop6=4&prop7=5&prop8=2024-11-30T15%3A35%3A45.9870000%2B03%3A00&prop9=2024-11-30&prop10=23%3A46%3A59&prop11=P756DT4H6M8.01S&prop12=AgQG&prop13=3adeb301-58f1-45c5-b820-ae5f4af13c89&prop14=127", serializedString);
}

[Fact]
Expand Down
29 changes: 29 additions & 0 deletions tests/serialization/json/Converters/JsonDateConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System;
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Kiota.Abstractions;

namespace Microsoft.Kiota.Serialization.Json.Tests.Converters;

/// <summary>
/// Converts a Date object or value to/from JSON.
/// </summary>
public class JsonDateConverter : JsonConverter<Date>
{
/// <inheritdoc />
public override Date Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> reader.TokenType == JsonTokenType.Null
? new Date()
: ReadInternal(ref reader);

/// <inheritdoc />
public override void Write(Utf8JsonWriter writer, Date value, JsonSerializerOptions options)
=> WriteInternal(writer, value);

private static Date ReadInternal(ref Utf8JsonReader reader)
=> new Date(DateTime.ParseExact(reader.GetString()!, "dd---MM---yyyy", CultureInfo.InvariantCulture));

private static void WriteInternal(Utf8JsonWriter writer, Date value)
=> writer.WriteStringValue($"{value.Day}---{value.Month}---{value.Year}");
}
28 changes: 28 additions & 0 deletions tests/serialization/json/Converters/JsonDateTimeOffsetConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Microsoft.Kiota.Serialization.Json.Tests.Converters;

/// <summary>
/// Converts a DateTimeOffset object or value to/from JSON.
/// </summary>
public class JsonDateTimeOffsetConverter : JsonConverter<DateTimeOffset>
{
/// <inheritdoc />
public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> reader.TokenType == JsonTokenType.Null
? new DateTimeOffset()
: ReadInternal(ref reader);

/// <inheritdoc />
public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options)
=> WriteInternal(writer, value);

private static DateTimeOffset ReadInternal(ref Utf8JsonReader reader)
=> DateTimeOffset.ParseExact(reader.GetString()!, "dd__MM__yyyyTHH_mm_ssZ", CultureInfo.InvariantCulture);

private static void WriteInternal(Utf8JsonWriter writer, DateTimeOffset value)
=> writer.WriteStringValue(value.ToString("dd__MM__yyyyTHH_mm_ssZ"));
}
29 changes: 29 additions & 0 deletions tests/serialization/json/Converters/JsonTimeConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System;
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Kiota.Abstractions;

namespace Microsoft.Kiota.Serialization.Json.Tests.Converters;

/// <summary>
/// Converts a Time object or value to/from JSON.
/// </summary>
public class JsonTimeConverter : JsonConverter<Time>
{
/// <inheritdoc />
public override Time Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> reader.TokenType == JsonTokenType.Null
? new Time()
: ReadInternal(ref reader);

/// <inheritdoc />
public override void Write(Utf8JsonWriter writer, Time value, JsonSerializerOptions options)
=> WriteInternal(writer, value);

private static Time ReadInternal(ref Utf8JsonReader reader)
=> new Time(DateTime.ParseExact(reader.GetString()!, "HH__mm__ss", CultureInfo.InvariantCulture));

private static void WriteInternal(Utf8JsonWriter writer, Time value)
=> writer.WriteStringValue($"{value.Hour}__{value.Minute}__{value.Second}");
}
210 changes: 210 additions & 0 deletions tests/serialization/json/JsonParseNodeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -322,5 +322,215 @@ public void GetCollectionOfNullableEnumValuesFromJson()
Assert.Equal(TestNamingEnum.Item2SubItem1, values[0]);
Assert.Equal(TestNamingEnum.Item3SubItem1, values[1]);
}

[Fact]
public void GetDateValue_ReturnNullWhenValueKindIsNotString()
{
// Arrange
using var jsonDocument = JsonDocument.Parse("{\"startDate\":\"2024-07-31\"}");
var parseNode = new JsonParseNode(jsonDocument.RootElement);

// Act
var result = parseNode.GetDateValue();

// Assert
Assert.Null(result);
}

[Fact]
public void GetDateValue_ReturnNullWhenDateParseFails()
{
// Arrange
using var jsonDocument = JsonDocument.Parse("\"2024-13-32\"");
var parseNode = new JsonParseNode(jsonDocument.RootElement);

// Act
var result = parseNode.GetDateValue();

// Assert
Assert.Null(result);
}

[Fact]
public void GetDateValue_ReturnDateWhenCustomConverterIsUsed()
{
// Arrange
var serializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.General)
{
Converters = { new JsonDateConverter() }
};
var serializationContext = new KiotaJsonSerializationContext(serializerOptions);

using var jsonDocument = JsonDocument.Parse("\"31---07---2024\"");
var parseNode = new JsonParseNode(jsonDocument.RootElement, serializationContext);

// Act
var result = parseNode.GetDateValue();

// Assert
Assert.NotNull(result);
Assert.Equal(31, result.Value.Day);
Assert.Equal(7, result.Value.Month);
Assert.Equal(2024, result.Value.Year);
}

[Fact]
public void GetDateValue_ReturnCorrectDate()
{
// Arrange
using var jsonDocument = JsonDocument.Parse("\"2024-07-31\"");
var parseNode = new JsonParseNode(jsonDocument.RootElement);

// Act
var result = parseNode.GetDateValue();

// Assert
Assert.NotNull(result);
Assert.Equal(31, result.Value.Day);
Assert.Equal(7, result.Value.Month);
Assert.Equal(2024, result.Value.Year);
}

[Fact]
public void GetTimeValue_ReturnNullWhenValueKindIsNotString()
{
// Arrange
using var jsonDocument = JsonDocument.Parse("{\"startTime\":\"12:34:56\"}");
var parseNode = new JsonParseNode(jsonDocument.RootElement);

// Act
var result = parseNode.GetTimeValue();

// Assert
Assert.Null(result);
}

[Fact]
public void GetTimeValue_ReturnNullWhenTimeParseFails()
{
// Arrange
using var jsonDocument = JsonDocument.Parse("\"12:60:56\"");
var parseNode = new JsonParseNode(jsonDocument.RootElement);

// Act
var result = parseNode.GetTimeValue();

// Assert
Assert.Null(result);
}

[Fact]
public void GetTimeValue_ReturnTimeWhenCustomConverterIsUsed()
{
// Arrange
var serializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.General)
{
Converters = { new JsonTimeConverter() }
};
var serializationContext = new KiotaJsonSerializationContext(serializerOptions);

using var jsonDocument = JsonDocument.Parse("\"12__34__56\"");
var parseNode = new JsonParseNode(jsonDocument.RootElement, serializationContext);

// Act
var result = parseNode.GetTimeValue();

// Assert
Assert.NotNull(result);
Assert.Equal(12, result.Value.Hour);
Assert.Equal(34, result.Value.Minute);
Assert.Equal(56, result.Value.Second);
}

[Fact]
public void GetTimeValue_ReturnTime()
{
// Arrange
using var jsonDocument = JsonDocument.Parse("\"12:34:56\"");
var parseNode = new JsonParseNode(jsonDocument.RootElement);

// Act
var result = parseNode.GetTimeValue();

// Assert
Assert.NotNull(result);
Assert.Equal(12, result.Value.Hour);
Assert.Equal(34, result.Value.Minute);
Assert.Equal(56, result.Value.Second);
}

[Fact]
public void GetDateTimeOffsetValue_ReturnNullWhenValueKindIsNotString()
{
// Arrange
using var jsonDocument = JsonDocument.Parse("{\"startDateTime\":\"2024-07-31\"}");
var parseNode = new JsonParseNode(jsonDocument.RootElement);

// Act
var result = parseNode.GetDateTimeOffsetValue();

// Assert
Assert.Null(result);
}

[Fact]
public void GetDateTimeOffsetValue_ReturnNullWhenDateTimeOffsetParseFails()
{
// Arrange
using var jsonDocument = JsonDocument.Parse("\"2024-13-32T12:34:56Z\"");
var parseNode = new JsonParseNode(jsonDocument.RootElement);

// Act
var result = parseNode.GetDateTimeOffsetValue();

// Assert
Assert.Null(result);
}

[Fact]
public void GetDateTimeOffsetValue_ReturnDateTimeOffsetWhenCustomConverterIsUsed()
{
// Arrange
var serializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.General)
{
Converters = { new JsonDateTimeOffsetConverter() }
};
var serializationContext = new KiotaJsonSerializationContext(serializerOptions);

using var jsonDocument = JsonDocument.Parse("\"31__07__2024T12_34_56Z\"");
var parseNode = new JsonParseNode(jsonDocument.RootElement, serializationContext);

// Act
var result = parseNode.GetDateTimeOffsetValue();

// Assert
Assert.NotNull(result);
Assert.Equal(31, result.Value.Day);
Assert.Equal(7, result.Value.Month);
Assert.Equal(2024, result.Value.Year);
Assert.Equal(12, result.Value.Hour);
Assert.Equal(34, result.Value.Minute);
Assert.Equal(56, result.Value.Second);
}

[Fact]
public void GetDateTimeOffsetValue_ReturnCorrectDateTimeOffset()
{
// Arrange
using var jsonDocument = JsonDocument.Parse("\"2024-07-31T12:34:56Z\"");
var parseNode = new JsonParseNode(jsonDocument.RootElement);

// Act
var result = parseNode.GetDateTimeOffsetValue();

// Assert
Assert.NotNull(result);
Assert.Equal(31, result.Value.Day);
Assert.Equal(7, result.Value.Month);
Assert.Equal(2024, result.Value.Year);
Assert.Equal(12, result.Value.Hour);
Assert.Equal(34, result.Value.Minute);
Assert.Equal(56, result.Value.Second);
}
}
}

0 comments on commit 9ccd477

Please sign in to comment.