Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,6 @@ The recommended commit types used are:
- __docs__ for documentation updates e.g. ReadMe update or code documentation updates
- __build__ for build system changes (gradle updates, external dependency updates)
- __ci__ for CI configuration file changes e.g. updating a pipeline
- __chore__ for miscallaneous non-sdk changesin the repo e.g. removing an unused file
- __chore__ for miscallaneous non-sdk changes in the repo e.g. removing an unused file

Adding an exclamation mark after the commit type (`feat!`) or footer with the prefix __BREAKING CHANGE:__ will cause an increment of the _major_ version.
20 changes: 15 additions & 5 deletions src/serialization/json/JsonParseNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@
using System.Reflection;
using System.Runtime.Serialization;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
using System.Xml;
using Microsoft.Kiota.Abstractions;
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Helpers;
using Microsoft.Kiota.Abstractions.Serialization;


#if NET5_0_OR_GREATER
using System.Diagnostics.CodeAnalysis;
#endif
Expand Down Expand Up @@ -87,42 +89,50 @@ public JsonParseNode(JsonElement node, KiotaJsonSerializationContext jsonSeriali
/// Get the int value from the json node
/// </summary>
/// <returns>A int value</returns>
public int? GetIntValue() => _jsonNode.ValueKind == JsonValueKind.Number
public int? GetIntValue() => IsParsableNumber(_jsonNode.ValueKind, _jsonSerializerContext.Options.NumberHandling)
? _jsonNode.Deserialize(_jsonSerializerContext.Int32)
: null;


/// <summary>
/// Get the float value from the json node
/// </summary>
/// <returns>A float value</returns>
public float? GetFloatValue() => _jsonNode.ValueKind == JsonValueKind.Number
public float? GetFloatValue() => IsParsableNumber(_jsonNode.ValueKind, _jsonSerializerContext.Options.NumberHandling)
? _jsonNode.Deserialize(_jsonSerializerContext.Single)
: null;

/// <summary>
/// Get the Long value from the json node
/// </summary>
/// <returns>A Long value</returns>
public long? GetLongValue() => _jsonNode.ValueKind == JsonValueKind.Number
public long? GetLongValue() => IsParsableNumber(_jsonNode.ValueKind, _jsonSerializerContext.Options.NumberHandling)
? _jsonNode.Deserialize(_jsonSerializerContext.Int64)
: null;

/// <summary>
/// Get the double value from the json node
/// </summary>
/// <returns>A double value</returns>
public double? GetDoubleValue() => _jsonNode.ValueKind == JsonValueKind.Number
public double? GetDoubleValue() => IsParsableNumber(_jsonNode.ValueKind, _jsonSerializerContext.Options.NumberHandling)
? _jsonNode.Deserialize(_jsonSerializerContext.Double)
: null;

/// <summary>
/// Get the decimal value from the json node
/// </summary>
/// <returns>A decimal value</returns>
public decimal? GetDecimalValue() => _jsonNode.ValueKind == JsonValueKind.Number
public decimal? GetDecimalValue() => IsParsableNumber(_jsonNode.ValueKind, _jsonSerializerContext.Options.NumberHandling)
? _jsonNode.Deserialize(_jsonSerializerContext.Decimal)
: null;

private static bool IsParsableNumber(JsonValueKind valueKind, JsonNumberHandling numberHandling) => valueKind switch
{
JsonValueKind.Number => true,
JsonValueKind.String when numberHandling.HasFlag(JsonNumberHandling.AllowReadingFromString) => true,
_ => false
};

/// <summary>
/// Get the guid value from the json node
/// </summary>
Expand Down
189 changes: 189 additions & 0 deletions tests/serialization/json/JsonParseNodeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Globalization;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using Microsoft.Kiota.Abstractions;
using Microsoft.Kiota.Abstractions.Serialization;
Expand Down Expand Up @@ -118,6 +119,13 @@ public JsonParseNodeTests()

private static readonly string TestUserCollectionString = $"[{TestUserJson}]";

private static readonly KiotaJsonSerializationContext ReadNumbersAsStringsContext = new KiotaJsonSerializationContext(
new JsonSerializerOptions()
{
NumberHandling = JsonNumberHandling.AllowReadingFromString
}
);

[Fact]
public void GetsEntityValueFromJson()
{
Expand Down Expand Up @@ -533,5 +541,186 @@ public void GetDateTimeOffsetValue_ReturnCorrectDateTimeOffset()
Assert.Equal(34, result.Value.Minute);
Assert.Equal(56, result.Value.Second);
}

[Theory]
[InlineData("42", 42)]
[InlineData("\"42\"", null)]
[InlineData("\"not-a-number\"", null)]
[InlineData("null", null)]
public void GetIntValue_CanReadNumber(string input, int? expected)
{
// Arrange
using var jsonDocument = JsonDocument.Parse(input);
var parseNode = new JsonParseNode(jsonDocument.RootElement);

// Act, Assert
Assert_CanReadNumber(parseNode.GetIntValue, expected);
}

[Theory]
[InlineData("42", 42, "")]
[InlineData("\"42\"", 42, "")]
[InlineData("\"not-a-number\"", null, "The JSON value could not be converted to System.Int32.")]
[InlineData("null", null, "")]
public void GetIntValue_CanReadNumber_AsString(string input, int? expectedValue, string? expectexpectedExceptionMessage)
{
// Arrange
using var jsonDocument = JsonDocument.Parse(input);
var parseNode = new JsonParseNode(jsonDocument.RootElement, ReadNumbersAsStringsContext);

// Act, Assert
Assert_CanReadNumber(parseNode.GetIntValue, expectedValue, expectexpectedExceptionMessage);
}

[Theory]
[InlineData("42", 42L)]
[InlineData("\"42\"", null)]
[InlineData("\"not-a-number\"", null)]
[InlineData("null", null)]
public void GetLongValue_CanReadNumber(string input, long? expected)
{
// Arrange
using var jsonDocument = JsonDocument.Parse(input);
var parseNode = new JsonParseNode(jsonDocument.RootElement);

// Act, Assert
Assert_CanReadNumber(parseNode.GetLongValue, expected);
}

[Theory]
[InlineData("42", 42L, null)]
[InlineData("\"42\"", 42L, null)]
[InlineData("\"not-a-number\"", null, "The JSON value could not be converted to System.Int64.")]
[InlineData("null", null, null)]
public void GetLongValue_CanReadNumber_AsString(string input, long? expectedValue, string? expectedExceptionMessage)
{
// Arrange
using var jsonDocument = JsonDocument.Parse(input);
var parseNode = new JsonParseNode(jsonDocument.RootElement, ReadNumbersAsStringsContext);

// Act, Assert
Assert_CanReadNumber(parseNode.GetLongValue, expectedValue, expectedExceptionMessage);
}

[Theory]
[InlineData("13.37", 13.37F)]
[InlineData("\"13.37\"", null)]
[InlineData("\"not-a-number\"", null)]
[InlineData("null", null)]
public void GetFloatValue_CanReadNumber(string input, float? expected)
{
// Arrange
using var jsonDocument = JsonDocument.Parse(input);
var parseNode = new JsonParseNode(jsonDocument.RootElement);

// Act, Assert
Assert_CanReadNumber(parseNode.GetFloatValue, expected);
}

[Theory]
[InlineData("13.37", 13.37F, "")]
[InlineData("\"13.37\"", 13.37F, "")]
[InlineData("\"not-a-number\"", null, "The JSON value could not be converted to System.Single.")]
[InlineData("null", null, "")]
public void GetFloatValue_CanReadNumber_AsString(string input, float? expectedValue, string? expectedExceptionMessage)
{
// Arrange
using var jsonDocument = JsonDocument.Parse(input);
var parseNode = new JsonParseNode(jsonDocument.RootElement, ReadNumbersAsStringsContext);

// Act, Assert
Assert_CanReadNumber(parseNode.GetFloatValue, expectedValue, expectedExceptionMessage);
}

[Theory]
[InlineData("13.37", 13.37D)]
[InlineData("\"13.37\"", null)]
[InlineData("\"not-a-number\"", null)]
[InlineData("null", null)]
public void GetDoubleValue_CanReadNumber(string input, double? expected)
{
// Arrange
using var jsonDocument = JsonDocument.Parse(input);
var parseNode = new JsonParseNode(jsonDocument.RootElement);

// Act, Assert
Assert_CanReadNumber(parseNode.GetDoubleValue, expected);
}

[Theory]
[InlineData("13.37", 13.37D)]
[InlineData("\"13.37\"", 13.37D)]
[InlineData("\"not-a-number\"", null, "The JSON value could not be converted to System.Double.")]
[InlineData("null", null)]
public void GetDoubleValue_CanReadNumber_AsString(string input, double? expectedValue, string? expectedExceptionMessage = null)
{
// Arrange
using var jsonDocument = JsonDocument.Parse(input);
var parseNode = new JsonParseNode(jsonDocument.RootElement, ReadNumbersAsStringsContext);

// Act, Assert
Assert_CanReadNumber(parseNode.GetDoubleValue, expectedValue, expectedExceptionMessage);
}

[Theory]
[InlineData("13.37", 13.37)]
[InlineData("\"13.37\"", null)]
[InlineData("\"not-a-number\"", null)]
[InlineData("null", null)]
public void GetDecimalValue_CanReadNumber(string input, double? expectedDouble)
{
// Arrange
var expectedValue = expectedDouble.HasValue
? Convert.ToDecimal(expectedDouble)
: default(decimal?)
; //13.37M is not supported as a constant expression in attributes

using var jsonDocument = JsonDocument.Parse(input);
var parseNode = new JsonParseNode(jsonDocument.RootElement);

Assert_CanReadNumber(parseNode.GetDecimalValue, expectedValue);
}

[Theory]
[InlineData("13.37", 13.37)]
[InlineData("\"13.37\"", 13.37)]
[InlineData("\"not-a-number\"", null, "The JSON value could not be converted to System.Decimal.")]
[InlineData("null", null)]
public void GetDecimalValue_CanReadNumber_AsString(string input, double? expectedDouble, string? expectedExceptionMessage = null)
{
// Arrange
var expected = expectedDouble.HasValue
? Convert.ToDecimal(expectedDouble)
: default(decimal?)
; //13.37M is not supported as a constant expression in attributes

using var jsonDocument = JsonDocument.Parse(input);
var parseNode = new JsonParseNode(jsonDocument.RootElement, ReadNumbersAsStringsContext);

// Act, Assert
Assert_CanReadNumber(parseNode.GetDecimalValue, expected, expectedExceptionMessage);
}

private static void Assert_CanReadNumber<TNumeric>(Func<TNumeric?> act, TNumeric? expectedValue, string? expectedExceptionMessage = null) where TNumeric : struct
{
if(string.IsNullOrEmpty(expectedExceptionMessage))
{
// Act
var actual = act();

// Assert
Assert.Equal(expectedValue.HasValue, actual.HasValue);
if(expectedValue.HasValue && actual.HasValue)
{
Assert.Equal(expectedValue.Value, actual.Value);
}
}
else
{
// Act, Assert
var exception = Assert.Throws<JsonException>(() => act());
Assert.StartsWith(expectedExceptionMessage, exception.Message);
}
}
}
}
Loading