You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
`JsonSchemaDefaultPropertyNaming` accepts any `NamingConvention` value (`AsDeclared`, `CamelCase`, `PascalCase`, `LowerSnakeCase`, `UpperSnakeCase`, `KebabCase`, `UpperKebabCase`). `JsonSchemaDefaultPropertyOrder` accepts `AsDeclared` or `ByName`. The `[GenerateJsonSchema]` attribute properties override these defaults per type.
Copy file name to clipboardExpand all lines: _docs/schema/schemagen/examples/refiner.md
+13-36Lines changed: 13 additions & 36 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -10,57 +10,34 @@ order: "01.06.4.4"
10
10
11
11
Sometimes, you may need to have custom logic that changes the generated schema in a way that can't be fulfilled with Generators, Intents, or Attributes.
12
12
13
-
As an example, this library handles nullability outside of these mechanisms by making use of a _refiner_.
14
-
15
-
This example shows how this kind of custom logic can be accomplished.
16
-
17
-
It first looks at the generated schema to determine whether it can add a `null` to the `type` keyword. To do this, it needs to look at a configuration option as well as a special `[Nullable(bool)]` attribute that is used to override the option.
13
+
As an illustration, consider a refiner that ensures every generated string schema requires at least one character, unless a `MinLength` is already present.
if (context.Intents.OfType<MinLengthIntent>().Any()) return;
50
29
51
-
if (context.Configuration.Nullability.HasFlag(Nullability.AllowForReferenceTypes) &&
52
-
!context.Type.IsValueType)
53
-
typeIntent.Type|=SchemaValueType.Null;
30
+
context.Intents.Add(newMinLengthIntent(1));
54
31
}
55
32
}
56
33
```
57
34
58
-
Because this refiner is defined in the library, it's added automatically. But to include your refiner in the generation process, you'll need to add it to the `Refiners` collection in the configuration options.
35
+
To include a refiner in the generation process, add it to the `Refiners` collection in the configuration.
A non-generic overload, `FromType(Type, ...)`, is also available for cases where the type is not known at compile time.
47
+
46
48
This method uses reflection and won't work with Native AOT.
47
49
48
50
## IMPORTANT {#schema-schemagen-disclaimer}
@@ -78,6 +80,9 @@ All of these and more are supplied via a set of attributes that can be applied t
78
80
-`MinItems`
79
81
-`MaxItems`
80
82
-`UniqueItems`
83
+
-`AdditionalItems`
84
+
- Objects
85
+
-`AdditionalProperties`
81
86
- All
82
87
-`Id`
83
88
-`Required` & `Nullable` (see below)
@@ -91,16 +96,17 @@ All of these and more are supplied via a set of attributes that can be applied t
91
96
-`WriteOnly`
92
97
- Conditional (see [Conditionals](./conditional-generation))
93
98
-`If`
94
-
-`Then`
95
-
-`Else`
99
+
-`IfEnum`
100
+
-`IfMin`
101
+
-`IfMax`
96
102
97
-
\* The `[Obsolete]` attribute is `System.Obsolete`. All of the others have been defined within this library. `System.ComponentModel.DataAnnotations` support is currently [in discussion](https://github.com/gregsdennis/json-everything/issues/143).
103
+
\* The `[Obsolete]` attribute is `System.Obsolete`. All of the others have been defined within this library. `System.ComponentModel.DataAnnotations` support is available via the separate [Data Annotations](./data-annotations) package.
98
104
99
105
\*\* The `[JsonExclude]` attribute functions equivalently to `[JsonIgnore]` (see below). It is included to allow generation to skip a property or an enum member while allowing serialization to consider it.
100
106
101
107
\*\*\* Even though the `const` and `default` keywords in JSON Schema can accept any JSON value, because they are attributes, `[Const]` and `[Default]` can only accept values which are compile-time constants.
102
108
103
-
> The `System.ComponentModel.DataAnnotations`annotations are not (and likely will not be) supported by this library. Defining the above attributes separately allows alignment with JSON Schema and separation of concerns between serialization and validation.
109
+
> `System.ComponentModel.DataAnnotations`attributes are not handled by this library directly. Support is provided via the separate [Data Annotations](./data-annotations) package.
104
110
{: .prompt-info }
105
111
106
112
Simply add the attributes directly to the properties and the corresponding keywords will be added to the schema.
@@ -276,23 +282,6 @@ To this end, the `[Required]` attribute will only be represented in generated sc
> Thislibrarywasunableto [detect](https://stackoverflow.com/a/62186551/878701) whether the consuming code has nullable reference types enabled. Therefore all reference types are considered nullable.
They can be applied directly to the configuration property:
320
311
@@ -369,6 +360,8 @@ Generating a properly descriptive-while-terse name is generally hard. This libr
369
360
> If you only want to handle specific types in your generator and are happy with the library's generation for others, simply return null from your generator and the library's generation will be used.
370
361
{: .prompt-tip }
371
362
363
+
To add a `$schema` keyword to generated schemas identifying the dialect, set `SchemaGeneratorConfiguration.DefaultDialect` to the appropriate URI.
364
+
372
365
## Extending support {#schema-schemagen-extension}
373
366
374
367
The above will work most of the time, but occasionally you may find that you need some additional support. Happily, the library is configured for you to provide that support yourself.
@@ -386,9 +379,9 @@ These do not _all_ need to be implemented.
386
379
387
380
These are the first phase of generation. When encountering a type, the system will find the first registered generator that can handle that type. The generator then creates keyword intents (see "Intents" below). The supported types list above is merely a list of the built-in generators.
388
381
389
-
To create a new generator, you'll need to implement the `ISchemaGenerator` interface and register it using the`GeneratorRegistry.Register()` static method. This will insert your generator at the top of the list so that it has priority.
382
+
To create a new generator, you'll need to implement the `ISchemaGenerator` interface. To register it globally, use`GeneratorRegistry.Register()`, which inserts it at the top of the list. To restrict it to a specific configuration instance, add it to `SchemaGeneratorConfiguration.Generators` instead.
390
383
391
-
> This means that the order your generators are registered is important: last one wins. So if you want one generator to have priority over another, register the higher priority one last.
384
+
> Registration order matters: last registered wins. If multiple generators can handle a given type, the one registered last will be used.
392
385
{: .prompt-warning }
393
386
394
387
This class doesn't need to be complex. Here's the implementation for the `BooleanSchemaGenerator`:
@@ -401,7 +394,7 @@ internal class BooleanSchemaGenerator : ISchemaGenerator
@@ -414,7 +407,7 @@ To explain _how_ it does, we need to discuss intents.
414
407
415
408
### The Context Object {#schema-schemagen-context}
416
409
417
-
The context holds all of the data you need to determine which intents need to be applied. It is defined by a base class, `SchemaGeneratorContextBase`, and two derivations, `TypeGenerationContext` and `MemberGenerationContext`.
410
+
The context holds all of the data you need to determine which intents need to be applied. It is defined by a base class, `SchemaGenerationContextBase`, and two derivations, `TypeGenerationContext` and `MemberGenerationContext`.
418
411
419
412
`TypeGenerationContext` represents generation of just a type (including attributes present on the type itself), whereas `MemberGenerationContext` represents generation of an object member, which will have a type (and its attributes) _and_ possibly additional attributes as a member.
420
413
@@ -424,14 +417,18 @@ The context holds all of the data you need to determine which intents need to be
424
417
The data exposed by contexts are:
425
418
426
419
-`Type` - the type for which a schema is being generated
427
-
-`ReferenceCount` - the number of times this context has been used
428
420
-`Intents` - the collection of intents that represent this type
429
-
-`Hash` - a hash value that can be used to identify this object
421
+
422
+
Each context also exposes an `Apply()` method that builds and returns a `JsonSchemaBuilder` with all intents applied.
423
+
424
+
The class also exposes two static instances, `True` and `False`, which represent boolean schemas.
430
425
431
426
`MemberGenerationContext` also defines:
432
427
433
-
-`BasedOn` - a context on which this context builds
428
+
-`BasedOn` - the `TypeGenerationContext` on which this member context builds
434
429
-`Attributes` - additional attributes defined on the member
430
+
-`NullableRef` - true when the member is declared as a nullable reference type
431
+
-`Parameter` - the index of the generic type argument this member applies to; -1 indicates the root type
435
432
436
433
### Intents {#schema-schemagen-intents}
437
434
@@ -475,16 +472,16 @@ The attribute itself is pretty simple. It's just a class that inherits from `At
if (!context.Type.IsNumber()&&!context.Type.IsNullableNumber()) return;
488
485
489
486
context.Intents.Add(newMaximumIntent(Value));
490
487
}
@@ -518,8 +515,8 @@ Refiners are called after all intents have been generated for each type, recursi
518
515
519
516
To implement a refiner, two methods will be needed:
520
517
521
-
-`bool ShouldRun(SchemaGeneratorContextBase)` which determines whether the refiner needs to run for the current generation iteration.
522
-
-`void Run(SchemaGeneratorContextBase)` which makes whatever modifications are needed.
518
+
-`bool ShouldRun(SchemaGenerationContextBase)` which determines whether the refiner needs to run for the current generation iteration.
519
+
-`void Run(SchemaGenerationContextBase)` which makes whatever modifications are needed.
523
520
524
521
Remember that a this point, you're stil working with intents. You can add new ones as well as modify or remove existing ones. You really have complete freedom within a refiner.
0 commit comments