Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add API support for multiple schema extension definitions #1578

Closed
wants to merge 1 commit into from
Closed
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
58 changes: 58 additions & 0 deletions src/NJsonSchema.Tests/Serialization/ExtensionDataTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,64 @@ public async Task When_extension_data_attribute_is_used_on_class_then_extension_
Assert.Equal(123, schema.ExtensionData["MyClass"]);
}

[Fact]
public async Task When_multi_extension_data_attribute_is_used_on_class_then_extension_data_property_is_set()
{
//// Arrange

//// Act
var schema = JsonSchema.FromType<MyMultiTest>();

//// Assert
Assert.Equal("myname", schema.ExtensionData["x-name"]);
Assert.Equal("red", schema.ExtensionData["x-color"]);
}

/// <summary>
/// Adds multiple extension data properties to a class or property.</summary>
/// <seealso cref="System.Attribute" />
[AttributeUsage(
AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.ReturnValue,
AllowMultiple = true)]
public class MultiJsonSchemaExtensionDataAttribute : Attribute, IMultiJsonSchemaExtensionDataAttribute
{
/// <summary>
/// Initializes a new instance of the <see cref="MultiJsonSchemaExtensionDataAttribute" /> class.</summary>
/// <param name="kvps">The key value pairs.</param>
public MultiJsonSchemaExtensionDataAttribute(string name, string color)
{
SchemaExtensionData = new Dictionary<string, object>
{
{"x-name", name},
{"x-color", color}
};
}

/// <summary>
/// Gets the extension data properties dictionary.
/// </summary>
public IDictionary<string, object> SchemaExtensionData { get; }
}
[MultiJsonSchemaExtensionData("myname", "red")]
public class MyMultiTest
{
[MultiJsonSchemaExtensionData("prop", "blue")]
public string Property { get; set; }
}

[Fact]
public async Task When_multi_extension_data_attribute_is_used_on_property_then_extension_data_property_is_set()
{
//// Arrange

//// Act
var schema = JsonSchema.FromType<MyMultiTest>();

//// Assert
Assert.Equal("prop", schema.Properties["Property"].ExtensionData["x-name"]);
Assert.Equal("blue", schema.Properties["Property"].ExtensionData["x-color"]);
}

[Fact]
public async Task When_extension_data_attribute_is_used_on_property_then_extension_data_property_is_set()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;

namespace NJsonSchema.Annotations
{
/// <summary>Interface to add multiple extension data property to a class or property, implementation needs to inherit from System.Attribute.</summary>
public interface IMultiJsonSchemaExtensionDataAttribute
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not a subject matter expect here (Rico is), but would it make sore sense just to make a breaking change to change the support to have dictionary in original interface so there wouldn't be two ways to achieve the same thing (adding one item can be done via dictionary or via old interface).

Copy link
Author

Choose a reason for hiding this comment

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

this definitely could be done, I took the more conservative approach because I didn't want to break existing functionality. I don't mind refactoring to do what you suggest if that's what is agreed upon

Copy link
Author

Choose a reason for hiding this comment

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

would appreciate a review @RicoSuter when you have some time

{
/// <summary>Gets the extension data properties dictionary.</summary>
IDictionary<string, object> SchemaExtensionData { get; }
}
}
35 changes: 32 additions & 3 deletions src/NJsonSchema/Generation/JsonSchemaGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1409,16 +1409,26 @@ private void ApplyRangeAttribute(JsonSchema schema, IEnumerable<Attribute> paren
private void ApplyTypeExtensionDataAttributes<TSchemaType>(TSchemaType schema, ContextualType contextualType) where TSchemaType : JsonSchema, new()
{
var extensionAttributes = contextualType.OriginalType.GetTypeInfo().GetCustomAttributes()
.Where(attribute => attribute is IJsonSchemaExtensionDataAttribute).ToArray();
.Where(attribute => attribute is IJsonSchemaExtensionDataAttribute
|| attribute is IMultiJsonSchemaExtensionDataAttribute).ToArray();

if (extensionAttributes.Any())
{
var extensionData = new Dictionary<string, object>();

foreach (var attribute in extensionAttributes)
{
var extensionAttribute = (IJsonSchemaExtensionDataAttribute)attribute;
extensionData.Add(extensionAttribute.Key, extensionAttribute.Value);
if (attribute is IJsonSchemaExtensionDataAttribute singleExt)
{
extensionData.Add(singleExt.Key, singleExt.Value);
}
else if (attribute is IMultiJsonSchemaExtensionDataAttribute multiExt)
{
foreach (var kvp in multiExt.SchemaExtensionData)
{
extensionData.Add(kvp.Key, kvp.Value);
}
}
}

schema.ExtensionData = extensionData;
Expand All @@ -1435,6 +1445,25 @@ private void ApplyPropertyExtensionDataAttributes(ContextualAccessorInfo accesso
{
propertySchema.ExtensionData = extensionDataAttributes.ToDictionary(a => a.Key, a => a.Value);
}

var multiExtensionDataAttributes = accessorInfo
.GetContextAttributes<IMultiJsonSchemaExtensionDataAttribute>()
.ToArray();

if (multiExtensionDataAttributes.Any())
{
if (propertySchema.ExtensionData == null)
{
propertySchema.ExtensionData = new Dictionary<string, object>();
}
foreach (var multiExt in multiExtensionDataAttributes)
{
foreach (var kvp in multiExt.SchemaExtensionData)
{
propertySchema.ExtensionData.Add(kvp.Key, kvp.Value);
}
}
}
}
}
}