Skip to content

Commit

Permalink
Merge pull request #698 from gregsdennis/various-fixes
Browse files Browse the repository at this point in the history
Various updates
  • Loading branch information
gregsdennis authored Mar 25, 2024
2 parents 441f9d5 + 5192b84 commit ccb621d
Show file tree
Hide file tree
Showing 19 changed files with 101 additions and 15 deletions.
31 changes: 31 additions & 0 deletions JsonSchema.Generation.Tests/ClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using Json.More;
using Json.Schema.Generation.Intents;
using NUnit.Framework;

Expand Down Expand Up @@ -323,4 +324,34 @@ public void Issue551_MinMaxItemsOnStringProperty()
Assert.AreEqual(1, schema.GetProperties()!["Value"].Keywords!.Count);
Assert.AreEqual("type", schema.GetProperties()!["Value"].Keywords!.First().Keyword());
}

private class Issue696_NullableDecimalWithMultipleOf
{
[Nullable(true)]
[MultipleOf(0.1)]
public decimal? Apr { get; set; }
}

[Test]
public void Issue696_MultipleOfMissingForNullableDecimal()
{
var expected = JsonNode.Parse(
"""
{
"type": "object",
"properties": {
"Apr": {
"type": ["number", "null"],
"multipleOf": 0.1
}
}
}
""");

JsonSchema schema = new JsonSchemaBuilder().FromType<Issue696_NullableDecimalWithMultipleOf>();
var schemaJson = JsonSerializer.SerializeToNode(schema, TestSerializerContext.Default.JsonSchema);
Console.WriteLine(schemaJson);

Assert.IsTrue(schemaJson.IsEquivalentTo(expected));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public ExclusiveMaximumAttribute(double value)

void IAttributeHandler.AddConstraints(SchemaGenerationContextBase context, Attribute attribute)
{
if (!context.Type.IsNumber()) return;
if (!context.Type.IsNumber() && !context.Type.IsNullableNumber()) return;

context.Intents.Add(new ExclusiveMaximumIntent(Value));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public ExclusiveMinimumAttribute(double value)

void IAttributeHandler.AddConstraints(SchemaGenerationContextBase context, Attribute attribute)
{
if (!context.Type.IsNumber()) return;
if (!context.Type.IsNumber() && !context.Type.IsNullableNumber()) return;

context.Intents.Add(new ExclusiveMinimumIntent(Value));
}
Expand Down
2 changes: 1 addition & 1 deletion JsonSchema.Generation/Attributes/IfMaxAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public IfMaxAttribute(string propertyName, double value, object? group)
{
if (PropertyType == null) return null;

if (PropertyType.IsNumber())
if (PropertyType.IsNumber() || PropertyType.IsNullableNumber())
{
if (IsExclusive) return new ExclusiveMaximumIntent(Value.ClampToDecimal());
return new MaximumIntent(Value.ClampToDecimal());
Expand Down
2 changes: 1 addition & 1 deletion JsonSchema.Generation/Attributes/IfMinAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public IfMinAttribute(string propertyName, double value, object? group)
{
if (PropertyType == null) return null;

if (PropertyType.IsNumber())
if (PropertyType.IsNumber() || PropertyType.IsNullableNumber())
{
if (IsExclusive) return new ExclusiveMinimumIntent(Value.ClampToDecimal());
return new MinimumIntent(Value.ClampToDecimal());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public void AddConstraints(SchemaGenerationContextBase context, Attribute attrib
var handlingAttribute = attribute as JsonNumberHandlingAttribute;
if (handlingAttribute == null) return;

if (!context.Type.IsNumber()) return;
if (!context.Type.IsNumber() && !context.Type.IsNullableNumber()) return;

var typeIntent = context.Intents.OfType<TypeIntent>().FirstOrDefault();

Expand Down
2 changes: 1 addition & 1 deletion JsonSchema.Generation/Attributes/MaximumAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public MaximumAttribute(double value)

void IAttributeHandler.AddConstraints(SchemaGenerationContextBase context, Attribute attribute)
{
if (!context.Type.IsNumber()) return;
if (!context.Type.IsNumber() && !context.Type.IsNullableNumber()) return;

context.Intents.Add(new MaximumIntent(Value));
}
Expand Down
2 changes: 1 addition & 1 deletion JsonSchema.Generation/Attributes/MinimumAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public MinimumAttribute(double value)

void IAttributeHandler.AddConstraints(SchemaGenerationContextBase context, Attribute attribute)
{
if (!context.Type.IsNumber()) return;
if (!context.Type.IsNumber() && !context.Type.IsNullableNumber()) return;

context.Intents.Add(new MinimumIntent(Value));
}
Expand Down
2 changes: 1 addition & 1 deletion JsonSchema.Generation/Attributes/MultipleOfAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public MultipleOfAttribute(double value)

void IAttributeHandler.AddConstraints(SchemaGenerationContextBase context, Attribute attribute)
{
if (!context.Type.IsNumber()) return;
if (!context.Type.IsNumber() && !context.Type.IsNullableNumber()) return;

context.Intents.Add(new MultipleOfIntent(Value));
}
Expand Down
4 changes: 2 additions & 2 deletions JsonSchema.Generation/JsonSchema.Generation.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<DocumentationFile>JsonSchema.Net.Generation.xml</DocumentationFile>
<LangVersion>latest</LangVersion>
<Version>4.1.1</Version>
<FileVersion>4.1.1.0</FileVersion>
<Version>4.2.0</Version>
<FileVersion>4.2.0.0</FileVersion>
<AssemblyVersion>4.0.0.0</AssemblyVersion>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
Expand Down
12 changes: 12 additions & 0 deletions JsonSchema.Generation/TypeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Reflection;
using System.Text;
using System.Text.Json.Serialization;
using Json.More;

namespace Json.Schema.Generation;

Expand Down Expand Up @@ -125,4 +126,15 @@ public static bool IsNullableValueType(this Type type)
{
return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
}

/// <summary>
/// Determines if the type is a nullable value type, i.e. <see cref="Nullable{T}"/>.
/// </summary>
/// <param name="type">The type</param>
/// <returns>True if the type is <see cref="Nullable{T}"/>; false otherwise.</returns>
public static bool IsNullableNumber(this Type type)
{
return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) &&
type.GetGenericArguments()[0].IsNumber();
}
}
6 changes: 3 additions & 3 deletions JsonSchema.Tests/SerializationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ public class SerializationTests
[TestCase("{\"additionalProperties\":true}")]
[TestCase("{\"additionalProperties\":false}")]
[TestCase("{\"additionalProperties\":{\"$id\":\"http://some.site/schema\"}}")]
[TestCase("{\"allOf\":[]}")]
[TestCase("{\"anyOf\":[]}")]
[TestCase("{\"allOf\":[{\"$id\":\"http://some.site/schema\"}]}")]
[TestCase("{\"anyOf\":[{\"$id\":\"http://some.site/schema\"}]}")]
[TestCase("{\"const\":\"some text\"}")]
[TestCase("{\"const\":9}")]
[TestCase("{\"const\":9.0}")]
Expand Down Expand Up @@ -74,7 +74,7 @@ public class SerializationTests
[TestCase("{\"minProperties\":1}")]
[TestCase("{\"multipleOf\":1}")]
[TestCase("{\"not\":{\"$id\":\"http://some.site/schema\"}}")]
[TestCase("{\"oneOf\":[]}")]
[TestCase("{\"oneOf\":[{\"$id\":\"http://some.site/schema\"}]}")]
[TestCase("{\"pattern\":\"^yes{1,3}$\"}")]
[TestCase("{\"patternProperties\":{\"foo\":{}}}")]
[TestCase("{\"prefixItems\":[{\"$id\":\"http://some.site/schema\"}]}")]
Expand Down
6 changes: 6 additions & 0 deletions JsonSchema/AllOfKeyword.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ public class AllOfKeyword : IJsonSchemaKeyword, ISchemaCollector
/// <param name="values">The set of schemas.</param>
public AllOfKeyword(params JsonSchema[] values)
{
if (values.Length == 0)
throw new ArgumentException($"'{Name}' requires at least one subschema");

Schemas = values.ToReadOnlyList() ?? throw new ArgumentNullException(nameof(values));
}

Expand All @@ -49,6 +52,9 @@ public AllOfKeyword(params JsonSchema[] values)
public AllOfKeyword(IEnumerable<JsonSchema> values)
{
Schemas = values.ToReadOnlyList() ?? throw new ArgumentNullException(nameof(values));

if (Schemas.Count == 0)
throw new ArgumentException($"'{Name}' requires at least one subschema");
}

/// <summary>
Expand Down
6 changes: 6 additions & 0 deletions JsonSchema/AnyOfKeyword.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ public class AnyOfKeyword : IJsonSchemaKeyword, ISchemaCollector
/// <param name="values">The set of schemas.</param>
public AnyOfKeyword(params JsonSchema[] values)
{
if (values.Length == 0)
throw new ArgumentException($"'{Name}' requires at least one subschema");

Schemas = values.ToReadOnlyList() ?? throw new ArgumentNullException(nameof(values));
}

Expand All @@ -49,6 +52,9 @@ public AnyOfKeyword(params JsonSchema[] values)
public AnyOfKeyword(IEnumerable<JsonSchema> values)
{
Schemas = values.ToReadOnlyList() ?? throw new ArgumentNullException(nameof(values));

if (Schemas.Count == 0)
throw new ArgumentException($"'{Name}' requires at least one subschema");
}

/// <summary>
Expand Down
4 changes: 2 additions & 2 deletions JsonSchema/JsonSchema.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
<PackageProjectUrl>https://github.com/gregsdennis/json-everything</PackageProjectUrl>
<RepositoryUrl>https://github.com/gregsdennis/json-everything</RepositoryUrl>
<PackageTags>json-schema validation schema json</PackageTags>
<Version>6.0.6</Version>
<FileVersion>6.0.6.0</FileVersion>
<Version>6.0.7</Version>
<FileVersion>6.0.7.0</FileVersion>
<AssemblyVersion>6.0.0.0</AssemblyVersion>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<AssemblyName>JsonSchema.Net</AssemblyName>
Expand Down
6 changes: 6 additions & 0 deletions JsonSchema/OneOfKeyword.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ public class OneOfKeyword : IJsonSchemaKeyword, ISchemaCollector
/// <param name="values">The keywords schema collection.</param>
public OneOfKeyword(params JsonSchema[] values)
{
if (values.Length == 0)
throw new ArgumentException($"'{Name}' requires at least one subschema");

Schemas = values.ToReadOnlyList() ?? throw new ArgumentNullException(nameof(values));
}

Expand All @@ -50,6 +53,9 @@ public OneOfKeyword(params JsonSchema[] values)
public OneOfKeyword(IEnumerable<JsonSchema> values)
{
Schemas = values.ToReadOnlyList();

if (Schemas.Count == 0)
throw new ArgumentException($"'{Name}' requires at least one subschema");
}

/// <summary>
Expand Down
6 changes: 6 additions & 0 deletions JsonSchema/PrefixItemsKeyword.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ public class PrefixItemsKeyword : IJsonSchemaKeyword, ISchemaCollector
/// </remarks>
public PrefixItemsKeyword(params JsonSchema[] values)
{
if (values.Length == 0)
throw new ArgumentException($"'{Name}' requires at least one subschema");

ArraySchemas = values.ToReadOnlyList();
}

Expand All @@ -52,6 +55,9 @@ public PrefixItemsKeyword(params JsonSchema[] values)
public PrefixItemsKeyword(IEnumerable<JsonSchema> values)
{
ArraySchemas = values.ToReadOnlyList();

if (ArraySchemas.Count == 0)
throw new ArgumentException($"'{Name}' requires at least one subschema");
}

/// <summary>
Expand Down
15 changes: 15 additions & 0 deletions tools/ApiDocsGenerator/release-notes/rn-json-schema-generation.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,21 @@ title: JsonSchema.Net.Generation
icon: fas fa-tag
order: "09.05"
---
# [4.2.0](https://github.com/gregsdennis/json-everything/pull/698) {#release-schema-4.2.0}

[#696](https://github.com/gregsdennis/json-everything/issues/696) - Number-based constraints ignored for nullable number types. Thanks to [@rhwork](https://github.com/rhwork) for reporting this.

The following have been updated:

- `[Minimum]`
- `[ExclusiveMinimum]`
- `[Maximum]`
- `[ExclusiveMaximum]`
- `[IfMin]`
- `[IfMax]`

Added extension method `bool IsNullableNumber(this Type)`.

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

Add support for multiple `[If]` attributes for the same property under the same group creating an `enum` keyword with all of the values.
Expand Down
4 changes: 4 additions & 0 deletions tools/ApiDocsGenerator/release-notes/rn-json-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ title: JsonSchema.Net
icon: fas fa-tag
order: "09.01"
---
# [6.0.7](https://github.com/gregsdennis/json-everything/pull/698) {#release-schema-6.0.7}

[#697](https://github.com/gregsdennis/json-everything/issues/697) - `allOf`, `anyOf`, `oneOf`, and `prefixItems` all require at least one subschema, but deserialization of these keywords didn't enforce this. Fix will throw `ArgumentException` when attempting to deserialize empty subschema sets. Thanks to [@Era-cell](https://github.com/Era-cell) for reporting this.

# [6.0.6](https://github.com/gregsdennis/json-everything/pull/695) {#release-schema-6.0.6}

Marked `Pre202012EvaluationResultsJsonConverter` as obsolete. The 2019-09/2020-12 output structure isn't very well specified, and making the current `EvaluationResults` architecture serialize to these formats isn't worth the effort.
Expand Down

0 comments on commit ccb621d

Please sign in to comment.