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

SchemaGen v1.4.0 #113

Merged
merged 8 commits into from May 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
89 changes: 89 additions & 0 deletions JsonSchema.Generation.Tests/AttributeHandlerTests.cs
@@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using NUnit.Framework;

namespace Json.Schema.Generation.Tests {
public class AttributeHandlerTests {

[Test]
public void DirectAttributeHandler() {
JsonSchema expected = new JsonSchemaBuilder()
.Type(SchemaValueType.Object)
.Properties(
("MyProperty", new JsonSchemaBuilder().Type(SchemaValueType.String).MaxLength(AttributeWithDirectHandler.MaxLength))
);

JsonSchema actual = new JsonSchemaBuilder().FromType<TypeWithCustomAttribute1>();

Assert.AreEqual(expected, actual);
}


[Test]
public void IndirectAttributeHandler() {
AttributeHandler.RemoveHandler<CustomAttributeHandler>();
AttributeHandler.AddHandler<CustomAttributeHandler>();

JsonSchema expected = new JsonSchemaBuilder()
.Type(SchemaValueType.Object)
.Properties(
("MyProperty", new JsonSchemaBuilder().Type(SchemaValueType.String).MaxLength(AttributeWithIndirectHandler.MaxLength))
);

JsonSchema actual = new JsonSchemaBuilder().FromType<TypeWithCustomAttribute2>();

Assert.AreEqual(expected, actual);
}


[AttributeUsage(AttributeTargets.Property)]
public class AttributeWithDirectHandler : Attribute, IAttributeHandler {

public const uint MaxLength = 100;

void IAttributeHandler.AddConstraints(SchemaGeneratorContext context) {
if (context.Attributes.Any(x => x.GetType() == typeof(AttributeWithDirectHandler))) {
context.Intents.Add(new Intents.MaxLengthIntent(MaxLength));
}
}

}


[AttributeUsage(AttributeTargets.Property)]
public class AttributeWithIndirectHandler : Attribute {

public const uint MaxLength = 200;

}


public class CustomAttributeHandler : IAttributeHandler {
void IAttributeHandler.AddConstraints(SchemaGeneratorContext context) {
if (context.Attributes.Any(x => x.GetType() == typeof(AttributeWithIndirectHandler))) {
context.Intents.Add(new Intents.MaxLengthIntent(AttributeWithIndirectHandler.MaxLength));
}
}
}


public class TypeWithCustomAttribute1 {

[AttributeWithDirectHandler]
public string MyProperty { get; set; }

}


public class TypeWithCustomAttribute2 {

[AttributeWithIndirectHandler]
public string MyProperty { get; set; }

}

}
}
51 changes: 51 additions & 0 deletions JsonSchema.Generation.Tests/PropertyOrderTests.cs
@@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;

namespace Json.Schema.Generation.Tests
{
public class PropertyOrderTests
{
private class SpecifiedOrder
{
public int Second { get; set; }
public int First { get; set; }
}

[Test]
public void PropertiesAsDeclared()
{
var config = new SchemaGeneratorConfiguration
{
PropertyOrder = PropertyOrder.AsDeclared
};

JsonSchema schema = new JsonSchemaBuilder()
.FromType<SpecifiedOrder>(config);

var properties = schema.Keywords.OfType<PropertiesKeyword>().Single();

Assert.AreEqual(nameof(SpecifiedOrder.Second), properties.Properties.Keys.First());
Assert.AreEqual(nameof(SpecifiedOrder.First), properties.Properties.Keys.Last());
}

[Test]
public void PropertiesByName()
{
var config = new SchemaGeneratorConfiguration
{
PropertyOrder = PropertyOrder.ByName
};

JsonSchema schema = new JsonSchemaBuilder()
.FromType<SpecifiedOrder>(config);

var properties = schema.Keywords.OfType<PropertiesKeyword>().Single();

Assert.AreEqual(nameof(SpecifiedOrder.First), properties.Properties.Keys.First());
Assert.AreEqual(nameof(SpecifiedOrder.Second), properties.Properties.Keys.Last());
}
}
}
4 changes: 2 additions & 2 deletions JsonSchema.Generation/AttributeHandler.cs
Expand Up @@ -27,7 +27,7 @@ public static class AttributeHandler
public static void AddHandler<T>()
where T : IAttributeHandler, new()
{
if (_handlers.Any(h => h.GetType() != typeof(T))) return;
if (_handlers.Any(h => h.GetType() == typeof(T))) return;

_handlers.Add(new T());
}
Expand All @@ -39,7 +39,7 @@ public static void AddHandler<T>()
public static void AddHandler(IAttributeHandler handler)
{
var handlerType = handler.GetType();
if (_handlers.Any(h => h.GetType() != handlerType)) return;
if (_handlers.Any(h => h.GetType() == handlerType)) return;

_handlers.Add(handler);
}
Expand Down
15 changes: 13 additions & 2 deletions JsonSchema.Generation/Generators/ObjectSchemaGenerator.cs
Expand Up @@ -30,8 +30,14 @@ public void AddConstraints(SchemaGeneratorContext context)
var membersToGenerate = propertiesToGenerate.Cast<MemberInfo>()
.Concat(fieldsToGenerate)
.Concat(hiddenPropertiesToGenerate)
.Concat(hiddenFieldsToGenerate)
.OrderBy(m => m.Name);
.Concat(hiddenFieldsToGenerate);

membersToGenerate = context.Configuration.PropertyOrder switch
{
PropertyOrder.AsDeclared => membersToGenerate.OrderBy(GetMemberSequenceValue),
PropertyOrder.ByName => membersToGenerate.OrderBy(m => m.Name),
_ => membersToGenerate
};

foreach (var member in membersToGenerate)
{
Expand Down Expand Up @@ -59,5 +65,10 @@ public void AddConstraints(SchemaGeneratorContext context)
if (required.Any())
context.Intents.Add(new RequiredIntent(required));
}

private static int GetMemberSequenceValue(MemberInfo member)
{
return member.MetadataToken;
}
}
}
4 changes: 2 additions & 2 deletions JsonSchema.Generation/JsonSchema.Generation.csproj
Expand Up @@ -15,8 +15,8 @@
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<DocumentationFile>JsonSchema.Net.Generation.xml</DocumentationFile>
<LangVersion>latest</LangVersion>
<Version>1.3.2</Version>
<FileVersion>1.3.2.0</FileVersion>
<Version>1.4.0</Version>
<FileVersion>1.4.0.0</FileVersion>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
Expand Down
30 changes: 25 additions & 5 deletions JsonSchema.Generation/JsonSchema.Net.Generation.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions JsonSchema.Generation/PropertyOrder.cs
@@ -0,0 +1,17 @@
namespace Json.Schema.Generation
{
/// <summary>
/// Indicates the sequence in which properties will be listed in the schema.
/// </summary>
public enum PropertyOrder
{
/// <summary>
/// Properties will be listed in the order they're declared in code.
/// </summary>
AsDeclared,
/// <summary>
/// Properties will be sorted by name, case-insensitive.
/// </summary>
ByName
}
}
4 changes: 4 additions & 0 deletions JsonSchema.Generation/SchemaGeneratorConfiguration.cs
Expand Up @@ -16,5 +16,9 @@ public class SchemaGeneratorConfiguration
/// A collection of generators in addition to the global set.
/// </summary>
public List<ISchemaGenerator> Generators { get; set; } = new List<ISchemaGenerator>();
/// <summary>
/// Gets or sets the order in which properties will be listed in the schema.
/// </summary>
public PropertyOrder PropertyOrder { get; set; }
}
}
6 changes: 6 additions & 0 deletions docs_source/release-notes/json-schema-generation.md
@@ -1,3 +1,9 @@
# [1.4.0](https://github.com/gregsdennis/json-everything/pull/113)

[#109](https://github.com/gregsdennis/json-everything/issues/109) - Add properties in the order they're declared in code. Credit to [@wazzamatazz](https://github.com/jaysvoboda)

[#112](https://github.com/gregsdennis/json-everything/issues/112) - Add properties in the order they're declared in code.

# [1.3.2](https://github.com/gregsdennis/json-everything/pull/105)

Fixes property name callout in `required` keyword. Credit to [@jaysvoboda](https://github.com/jaysvoboda) for finding and fixing this.
Expand Down
7 changes: 7 additions & 0 deletions docs_source/usage/schema-generation.md
Expand Up @@ -77,6 +77,11 @@ The `minimum` is applied to the `items` because that keyword is not relevant for

***NOTE** This means that the generator will have trouble determining where to apply keywords to properties like `List<List<T>>` because the attributes could be relevant for both the outer and inner lists.*

The generator also supports these built-in attributes:

- `JsonPropertyName` - supports custom property naming
- `JsonNumberHandling` - supports allowing numeric values in strings or only as numbers as well as allowing the `NaN`, `Infinity`, and `-Infinity` values.

The generator will handle most common types:

- numeric types (`int`, `decimal`, etc.)
Expand All @@ -94,6 +99,8 @@ The generator will handle most common types:

For POCOs, currently only read/write properties are converted. Future versions of this library may also support read-only or write-only by adding the `readOnly` and `writeOnly` keywords, respectively.

Lastly, property names will either be listed as declared in code (default) or sorted by name. This is controlled via the `SchemaGenerationConfiguration.PropertyOrder` property.

### Additional built-in support

There are a couple advanced features that bear mentioning.
Expand Down