mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
enable enum serialization as strings and relaxed json escaping (#1267)
This commit is contained in:
committed by
GitHub
Unverified
parent
8bb1266020
commit
1cff86b4ff
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
@@ -37,10 +38,17 @@ internal static partial class AgentJsonUtilities
|
||||
private static JsonSerializerOptions CreateDefaultOptions()
|
||||
{
|
||||
// Copy the configuration from the source generated context.
|
||||
JsonSerializerOptions options = new(JsonContext.Default.Options);
|
||||
JsonSerializerOptions options = new(JsonContext.Default.Options)
|
||||
{
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, // same as in AgentAbstractionsJsonUtilities and AIJsonUtilities
|
||||
};
|
||||
|
||||
// Chain with all supported types from Microsoft.Extensions.AI.Abstractions.
|
||||
options.TypeInfoResolverChain.Add(AgentAbstractionsJsonUtilities.DefaultOptions.TypeInfoResolver!);
|
||||
if (JsonSerializer.IsReflectionEnabledByDefault)
|
||||
{
|
||||
options.Converters.Add(new JsonStringEnumConverter());
|
||||
}
|
||||
|
||||
options.MakeReadOnly();
|
||||
return options;
|
||||
@@ -48,6 +56,7 @@ internal static partial class AgentJsonUtilities
|
||||
|
||||
// Keep in sync with CreateDefaultOptions above.
|
||||
[JsonSourceGenerationOptions(JsonSerializerDefaults.Web,
|
||||
UseStringEnumConverter = true,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||
NumberHandling = JsonNumberHandling.AllowReadingFromString)]
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
#pragma warning disable CA1812 // Avoid uninstantiated internal classes
|
||||
|
||||
namespace Microsoft.Agents.AI.UnitTests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for <see cref="AgentJsonUtilities"/>
|
||||
/// </summary>
|
||||
public class AgentJsonUtilitiesTests
|
||||
{
|
||||
[Fact]
|
||||
public void DefaultOptions_HasExpectedConfiguration()
|
||||
{
|
||||
var options = AgentJsonUtilities.DefaultOptions;
|
||||
|
||||
// Must be read-only singleton.
|
||||
Assert.NotNull(options);
|
||||
Assert.Same(options, AgentJsonUtilities.DefaultOptions);
|
||||
Assert.True(options.IsReadOnly);
|
||||
|
||||
// Must conform to JsonSerializerDefaults.Web
|
||||
Assert.Equal(JsonNamingPolicy.CamelCase, options.PropertyNamingPolicy);
|
||||
Assert.True(options.PropertyNameCaseInsensitive);
|
||||
Assert.Equal(JsonNumberHandling.AllowReadingFromString, options.NumberHandling);
|
||||
|
||||
// Additional settings
|
||||
Assert.Equal(JsonIgnoreCondition.WhenWritingNull, options.DefaultIgnoreCondition);
|
||||
Assert.Same(JavaScriptEncoder.UnsafeRelaxedJsonEscaping, options.Encoder);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("<script>alert('XSS')</script>", "<script>alert('XSS')</script>")]
|
||||
[InlineData("""{"forecast":"sunny", "temperature":"75"}""", """{\"forecast\":\"sunny\", \"temperature\":\"75\"}""")]
|
||||
[InlineData("""{"message":"Πάντα ῥεῖ."}""", """{\"message\":\"Πάντα ῥεῖ.\"}""")]
|
||||
[InlineData("""{"message":"七転び八起き"}""", """{\"message\":\"七転び八起き\"}""")]
|
||||
[InlineData("""☺️🤖🌍𝄞""", """☺️\uD83E\uDD16\uD83C\uDF0D\uD834\uDD1E""")]
|
||||
public void DefaultOptions_UsesExpectedEscaping(string input, string expectedJsonString)
|
||||
{
|
||||
var options = AgentJsonUtilities.DefaultOptions;
|
||||
string json = JsonSerializer.Serialize(input, options);
|
||||
Assert.Equal($@"""{expectedJsonString}""", json);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultOptions_UsesReflectionWhenDefault()
|
||||
{
|
||||
Type anonType = new { Name = 42 }.GetType();
|
||||
Assert.Equal(JsonSerializer.IsReflectionEnabledByDefault, AgentJsonUtilities.DefaultOptions.TryGetTypeInfo(anonType, out _));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultOptions_AllowsReadingNumbersFromStrings_AndOmitsNulls()
|
||||
{
|
||||
var obj = JsonSerializer.Deserialize<NumberContainer>(
|
||||
"{\"value\":\"42\",\"optional\":null}", // value as string, optional null
|
||||
AgentJsonUtilities.DefaultOptions);
|
||||
Assert.NotNull(obj);
|
||||
Assert.Equal(42, obj!.Value);
|
||||
Assert.Null(obj.Optional);
|
||||
Assert.Equal("{\"value\":42}",
|
||||
JsonSerializer.Serialize(obj, AgentJsonUtilities.DefaultOptions)); // null omitted
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultOptions_SerializesEnumsAsStrings()
|
||||
{
|
||||
Assert.Equal("\"Monday\"", JsonSerializer.Serialize(DayOfWeek.Monday, AgentJsonUtilities.DefaultOptions));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultOptions_UsesCamelCasePropertyNames_ForAgentRunResponse()
|
||||
{
|
||||
var response = new AgentRunResponse(new ChatMessage(ChatRole.Assistant, "Hello"));
|
||||
string json = JsonSerializer.Serialize(response, AgentJsonUtilities.DefaultOptions);
|
||||
Assert.Contains("\"messages\"", json);
|
||||
Assert.DoesNotContain("\"Messages\"", json);
|
||||
}
|
||||
|
||||
private sealed class NumberContainer
|
||||
{
|
||||
public int Value { get; set; }
|
||||
public string? Optional { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user