Skip to content

Replace JsonSchema.Net with NJsonSchema#295

Open
rajan-chari wants to merge 3 commits intomainfrom
feature/replace-jsonschema-with-njsonschema
Open

Replace JsonSchema.Net with NJsonSchema#295
rajan-chari wants to merge 3 commits intomainfrom
feature/replace-jsonschema-with-njsonschema

Conversation

@rajan-chari
Copy link
Contributor

@rajan-chari rajan-chari commented Jan 28, 2026

Summary

  • Replace JsonSchema.Net with NJsonSchema (MIT) for JSON schema validation ahead of upcoming JsonSchema.Net license change
  • Introduce IJsonSchema abstraction interface to decouple the public API from the specific schema library
  • Add equivalence tests comparing both libraries to validate behavioral compatibility

Changes

  • New files:

    • IJsonSchema.cs - Abstraction interface for JSON schema operations
    • JsonSchemaWrapper.cs - NJsonSchema-based implementation
    • JsonSchemaEquivalenceTests.cs - Tests comparing both libraries
    • (JsonSchema dependency and JsonSchemaEquivalenceTests.cs will be removed once this PR is merged)
  • Modified files:

    • Function.cs - Use IJsonSchema instead of JsonSchema
    • ChatPrompt.Chain.cs / ChatPrompt.Functions.cs - Use abstraction
    • ChatTool.cs (OpenAI) - Use abstraction
    • McpClientPlugin.cs - Use abstraction

Breaking Change

IFunction.Parameters type changes from JsonSchema? to IJsonSchema?

User migration required if:

  • Passing JsonSchema to Function constructor → use JsonSchemaWrapper.FromJson() or JsonSchemaWrapper.FromType()
  • Reading Parameters and using JsonSchema methods → use IJsonSchema methods (Validate(), ToJson())

No changes needed if:

  • Using fluent API with auto-generated schemas: Function(name, description, handler)

Test plan

  • All existing tests pass (582 tests)
  • New equivalence tests verify both libraries validate identically
  • Manual verification with Samples.Lights

🤖 Generated with Claude Code

rajan-chari and others added 3 commits January 28, 2026 09:01
Switch from JsonSchema.Net (LGPL-3.0) to NJsonSchema (MIT) for JSON schema
validation. Introduces IJsonSchema abstraction to decouple the public API
from the specific schema library implementation.

- Add IJsonSchema interface and JsonSchemaWrapper implementation
- Update Function, ChatPrompt, and related classes to use abstraction
- Add explicit Humanizer.Core dependency (was transitive via JsonSchema.Net)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove unused using directives
- Add missing newlines at end of files
- Fix whitespace formatting in tests

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add tests that compare behavior between JsonSchema.Net and NJsonSchema
to validate the migration maintains compatibility. Tests cover:
- Schema generation from primitive and complex types
- Schema parsing from JSON strings
- Validation of required properties and type mismatches
- Function parameter schema generation patterns

Temporarily adds JsonSchema.Net (v7.3.4) and JsonSchema.Net.Generation
(v5.0.0) as test-only dependencies. These can be removed after the
migration is validated.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@rajan-chari rajan-chari marked this pull request as ready for review January 28, 2026 22:49
Copilot AI review requested due to automatic review settings January 28, 2026 22:49
@rajan-chari rajan-chari requested a review from aacebo January 28, 2026 22:50
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request replaces the JsonSchema.Net library with NJsonSchema (MIT licensed) to avoid an upcoming license change in JsonSchema.Net. The PR introduces an IJsonSchema abstraction interface to decouple the public API from the specific schema library implementation, making future migrations easier.

Changes:

  • Introduced IJsonSchema abstraction interface with JsonSchemaWrapper implementation using NJsonSchema
  • Updated IFunction.Parameters type from JsonSchema? to IJsonSchema? (breaking change)
  • Added comprehensive equivalence tests to validate behavioral compatibility between the two libraries
  • Removed unused imports and cleaned up trailing whitespace across sample files

Reviewed changes

Copilot reviewed 12 out of 19 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
Libraries/Microsoft.Teams.AI/Schema/IJsonSchema.cs New abstraction interface for JSON schema validation
Libraries/Microsoft.Teams.AI/Schema/JsonSchemaWrapper.cs NJsonSchema-based implementation of IJsonSchema
Libraries/Microsoft.Teams.AI/Function.cs Updated to use IJsonSchema interface instead of JsonSchema
Libraries/Microsoft.Teams.AI/Prompts/ChatPrompt/ChatPrompt.Functions.cs Updated Function method signature to use IJsonSchema
Libraries/Microsoft.Teams.AI/Prompts/ChatPrompt/ChatPrompt.Chain.cs Updated to use JsonSchemaWrapper.CreateObjectSchema
Libraries/Microsoft.Teams.AI/Microsoft.Teams.AI.csproj Replaced JsonSchema.Net packages with NJsonSchema and added explicit Humanizer.Core reference
Libraries/Microsoft.Teams.AI.Models.OpenAI/Extensions/ChatTool.cs Updated to use IJsonSchema methods (ToJson)
Libraries/Microsoft.Teams.Plugins/Microsoft.Teams.Plugins.External/Microsoft.Teams.Plugins.External.McpClient/McpClientPlugin.cs Updated to use JsonSchemaWrapper.FromJson
Tests/Microsoft.Teams.AI.Tests/JsonSchemaEquivalenceTests.cs New test file with equivalence tests comparing both libraries
Tests/Microsoft.Teams.AI.Tests/Microsoft.Teams.AI.Tests.csproj Added JsonSchema.Net packages for equivalence testing
Multiple sample and test files Whitespace cleanup and removal of unused imports

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +42 to +52
foreach (var (name, propSchema, required) in properties)
{
if (propSchema is JsonSchemaWrapper wrapper)
{
var property = new JsonSchemaProperty
{
Type = wrapper._schema.Type,
Description = wrapper._schema.Description
};
resultSchema.Properties.Add(name, property);
}
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CreateObjectSchema method silently skips properties when propSchema is not a JsonSchemaWrapper instance. This could lead to incomplete schema definitions if an implementation of IJsonSchema other than JsonSchemaWrapper is passed. Consider either throwing an exception when propSchema is not a JsonSchemaWrapper, or handling all IJsonSchema implementations properly. Alternatively, document this limitation in the method's XML documentation.

Copilot uses AI. Check for mistakes.
/// </summary>
public static IJsonSchema FromJson(string json)
{
var schema = JsonSchema.FromJsonAsync(json).GetAwaiter().GetResult();
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using GetAwaiter().GetResult() can cause deadlocks in certain synchronization contexts (e.g., UI or ASP.NET contexts before .NET Core). Consider making this method async and returning Task<IJsonSchema>, or use ConfigureAwait(false) before GetAwaiter() to reduce the risk of deadlocks. This pattern is used elsewhere in the codebase for consistency, but the deadlock risk should be considered.

Suggested change
var schema = JsonSchema.FromJsonAsync(json).GetAwaiter().GetResult();
var schema = JsonSchema.FromJsonAsync(json).ConfigureAwait(false).GetAwaiter().GetResult();

Copilot uses AI. Check for mistakes.
Comment on lines +49 to +50
Description = wrapper._schema.Description
};
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JsonSchemaProperty creation only copies Type and Description from the wrapped schema, but it doesn't copy other potentially important properties like Format, Pattern, MinLength, MaxLength, Minimum, Maximum, Enum values, etc. This could result in incomplete validation when complex schemas are used as properties. Consider either using the entire wrapped schema directly or copying all relevant schema properties.

Suggested change
Description = wrapper._schema.Description
};
Description = wrapper._schema.Description,
Format = wrapper._schema.Format,
Pattern = wrapper._schema.Pattern,
MinLength = wrapper._schema.MinLength,
MaxLength = wrapper._schema.MaxLength,
Minimum = wrapper._schema.Minimum,
Maximum = wrapper._schema.Maximum
};
foreach (var enumValue in wrapper._schema.Enumeration)
{
property.Enumeration.Add(enumValue);
}

Copilot uses AI. Check for mistakes.
Comment on lines 54 to +56
[JsonPropertyName("parameters")]
[JsonPropertyOrder(2)]
public JsonSchema? Parameters { get; set; }
public IJsonSchema? Parameters { get; set; }
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The IJsonSchema interface and JsonSchemaWrapper class lack a JsonConverter attribute or implementation. When Function.ToString() is called (line 118), it serializes the Parameters property which is of type IJsonSchema. Without a custom JsonConverter, the default serializer may not properly serialize the internal NJsonSchema.JsonSchema object, potentially resulting in incomplete or incorrect JSON output. Consider adding a JsonConverter for IJsonSchema that calls ToJson() on the instance.

Copilot uses AI. Check for mistakes.
Comment on lines +52 to +56
}

if (required)
{
resultSchema.RequiredProperties.Add(name);
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CreateObjectSchema method adds properties to RequiredProperties regardless of whether the property was successfully added to Properties. If a property's schema is not a JsonSchemaWrapper (and is thus skipped at lines 44-52), it will still be added to RequiredProperties at line 56. This inconsistency could lead to validation errors where the schema requires properties that don't exist in the schema definition.

Suggested change
}
if (required)
{
resultSchema.RequiredProperties.Add(name);
if (required)
{
resultSchema.RequiredProperties.Add(name);
}

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants