Skip to content

Commit

Permalink
Merge pull request #113 from gregsdennis/schemagen-v1.4.0
Browse files Browse the repository at this point in the history
SchemaGen v1.4.0
  • Loading branch information
gregsdennis committed May 21, 2021
2 parents 670a119 + 80ffb91 commit 4f12134
Show file tree
Hide file tree
Showing 10 changed files with 216 additions and 11 deletions.
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

0 comments on commit 4f12134

Please sign in to comment.