Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ internal static class KernelJsonSchemaBuilder
private static JsonSerializerOptions? s_options;
internal static readonly AIJsonSchemaCreateOptions s_schemaOptions = new();

[RequiresUnreferencedCode("Uses JsonStringEnumConverter and DefaultJsonTypeInfoResolver classes, making it incompatible with AOT scenarios.")]
[RequiresDynamicCode("Uses JsonStringEnumConverter and DefaultJsonTypeInfoResolver classes, making it incompatible with AOT scenarios.")]
internal static JsonSerializerOptions GetDefaultJsonSerializerOptions() => GetDefaultOptions();

private static readonly JsonElement s_trueSchemaAsObject = JsonElement.Parse("{}");
private static readonly JsonElement s_falseSchemaAsObject = JsonElement.Parse("""{"not":true}""");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -789,18 +789,20 @@ private static bool TryToDeserializeValue(object value, Type targetType, JsonSer
{
try
{
JsonSerializerOptions effectiveOptions = jsonSerializerOptions ?? KernelJsonSchemaBuilder.GetDefaultJsonSerializerOptions();

deserializedValue = value switch
{
JsonDocument document => document.Deserialize(targetType, jsonSerializerOptions),
JsonNode node => node.Deserialize(targetType, jsonSerializerOptions),
JsonElement element => element.Deserialize(targetType, jsonSerializerOptions),
JsonDocument document => document.Deserialize(targetType, effectiveOptions),
JsonNode node => node.Deserialize(targetType, effectiveOptions),
JsonElement element => element.Deserialize(targetType, effectiveOptions),
// The JSON can be represented by other data types from various libraries. For example, JObject, JToken, and JValue from the Newtonsoft.Json library.
// Since we don't take dependencies on these libraries and don't have access to the types here,
// the only way to deserialize those types is to convert them to a string first by calling the 'ToString' method.
// Attempting to use the 'JsonSerializer.Serialize' method, instead of calling the 'ToString' directly on those types, can lead to unpredictable outcomes.
// For instance, the JObject for { "id": 28 } JSON is serialized into the string "{ "Id": [] }", and the deserialization fails with the
// following exception - "The JSON value could not be converted to System.Int32. Path: $.Id | LineNumber: 0 | BytePositionInLine: 7."
_ => JsonSerializer.Deserialize(value.ToString()!, targetType, jsonSerializerOptions)
_ => JsonSerializer.Deserialize(value.ToString()!, targetType, effectiveOptions)
};

return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1413,13 +1413,31 @@ public async Task ItCanDeserializeJsonStringAsync()
var func = KernelFunctionFactory.CreateFromMethod((CustomTypeForJsonTests param) => { actualArgValue = param; });

// Act
var res = await func.InvokeAsync(this._kernel, new() { ["param"] = jsonString });
_ = await func.InvokeAsync(this._kernel, new() { ["param"] = jsonString });

// Assert
Assert.NotNull(actualArgValue);
Assert.Equal(28, actualArgValue.Id);
}

[Fact]
public async Task ItCanDeserializeStringEnumsWithDefaultJsonOptionsAsync()
{
// Arrange
var jsonString = @"[{""unit"":""hours""}]";
Reminder[]? actualArgValue = null;

var func = KernelFunctionFactory.CreateFromMethod((Reminder[] param) => { actualArgValue = param; });

// Act
_ = await func.InvokeAsync(this._kernel, new() { ["param"] = jsonString });

// Assert
Assert.NotNull(actualArgValue);
Assert.Single(actualArgValue);
Assert.Equal(ReminderUnit.Hours, actualArgValue[0].Unit);
}

[Fact]
public async Task ItCanDeserializeThirdPartyJsonPrimitivesAsync()
{
Expand Down Expand Up @@ -1586,6 +1604,19 @@ private sealed class CustomTypeForJsonTests
public int Id { get; set; }
}

private sealed class Reminder
{
[JsonPropertyName("unit")]
public ReminderUnit Unit { get; set; }
}

private enum ReminderUnit
{
Minutes,
Hours,
Days
}

private sealed class ThirdPartyJsonPrimitive(string jsonToReturn)
{
public override string ToString() => jsonToReturn;
Expand Down
Loading