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
Copy file name to clipboardexpand all lines: _docs/schema/schemagen/schema-generation.md
+47-46
Original file line number
Diff line number
Diff line change
@@ -16,7 +16,7 @@ var schema = schemaBuilder.FromType<MyType>().Build();
16
16
17
17
Done.
18
18
19
-
> The validating converter described in this document requires AOT-incompatible reflection to operate, so it will not be usable in a Native AOT context.
19
+
> The validating converter described in this document requires AOT-incompatible reflection to operate, so it may cause errors in a Native AOT context.
20
20
{: .prompt-warning}
21
21
22
22
## IMPORTANT {#schema-schemagen-disclaimer}
@@ -79,15 +79,15 @@ All of these and more are supplied via a set of attributes that can be applied t
79
79
80
80
Simply add the attributes directly to the properties and the corresponding keywords will be added to the schema.
81
81
82
-
For properties typed with generic collections, like `List<T>`, the schema will automatically generate an `items` keyword and generate a schema for the indicated `T`. If your `T` is a numeric value or a string, then you can also apply the relevant attributes and they'll be applied in the `items` subschema.
82
+
For properties typed with generic collections, like `List<T>`, the schema will automatically generate an `items` keyword and generate a schema for the indicated `T`. To specify that attributes should be applied to a generic parameter, use the `GenericParameter` property, and identify which parameter is to be attributed use a zero-based numeric index.
83
83
84
84
For example, this object:
85
85
86
86
```c#
87
87
classMyClass
88
88
{
89
89
[UniqueItems(true)]
90
-
[Minimum(10)]
90
+
[Minimum(10, GenericParameter=0)]
91
91
publicList<int> MyList{ get; set; }
92
92
}
93
93
```
@@ -110,11 +110,6 @@ will be translated to this schema:
110
110
}
111
111
```
112
112
113
-
The `minimum` is applied to the `items` because that keyword is not relevant for an array.
114
-
115
-
> 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.
116
-
{: .prompt-info }
117
-
118
113
The generator also supports these .Net-defined attributes:
119
114
120
115
-`JsonPropertyName` - supports custom property naming (more on naming below)
@@ -253,7 +248,11 @@ Those familiar with .Net validation will recognize that having `[Required]` on y
253
248
254
249
Tothisend, the `[Required]` attributewillonlyberepresentedingeneratedschemasinthe `required` keyword.
255
250
256
-
However, for nullable types, it may or may not be appropriate to include `null` in the `type` keyword. JsonSchema.Net.Generation controls this behavior via the `SchemaGeneratorConfiguration.Nullability` option with individual properties being overrideable via the `[Nullable(bool)]` attribute.
> Thislibrary[cannotdetect](https://stackoverflow.com/a/62186551/878701) whether the consuming code has nullable reference types enabled. Therefore all reference types are considered nullable.
264
+
> Thislibrarywasunableto [detect](https://stackoverflow.com/a/62186551/878701) whether the consuming code has nullable reference types enabled. Therefore all reference types are considered nullable.
Secondly, the system will collect type subschemas into a `$defs` keyword at the root. The locations with these common subschemas will be replaced by a `$ref` that points to the appropriate entry in `$defs`. When a definition is only referenced in one location, that definition will be re-integrated into the reference location. For example, instead of
311
+
312
+
```json
313
+
{
314
+
"type": "object",
315
+
"properties": {
316
+
"foo": { "$ref": "#/$defs/listOfString" }
317
+
},
318
+
"$defs": {
319
+
"listOfString": {
320
+
"type": "array",
321
+
"items": { "type": "string" }
322
+
}
323
+
}
324
+
}
325
+
```
326
+
327
+
you'll get
328
+
329
+
```json
330
+
{
331
+
"type": "object",
332
+
"properties": {
333
+
"foo": {
334
+
"type": "array",
335
+
"items": { "type": "string" }
336
+
}
337
+
}
338
+
}
339
+
```
312
340
313
-
Generatingaproperlydescriptive-while-tersenameishard. Thislibrarymakesafairattemptatit, generatingnameslike `myType` for `MyType` and `arrayOfInteger` for `int[]` or `List<int>`. Ifthisprovesinsufficientforyourneeds, implementyourownnamingasan `ITypeNameGenerator` andassignitto `SchemaGenerationContextOptimizer.TypeNameGenerator`.
341
+
Generating a properly descriptive-while-terse name is generally hard. This library makes a fair attempt at it, generating names like `myType` for `MyType` and `arrayOfInteger` for `int[]` or `List<int>`. If this proves insufficient for your needs, implement your own naming as an `ITypeNameGenerator` and assign it to `SchemaGeneratorConfiguration.TypeNameGenerator`.
314
342
315
343
> 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.
316
344
{: .prompt-tip }
@@ -407,40 +435,6 @@ public class TypeIntent : ISchemaKeywordIntent
407
435
408
436
See? The `Apply()` method just takes the builder, and adds a keyword with the data that it already collected. Pretty easy.
409
437
410
-
>Inv1.xofthelibrary, implementingtheequalitymethods (`Equals()` and `GetHashCode()`) wasrequired. Asofv2.0, thisisunnecessary.
411
-
{: .prompt-info }
412
-
413
-
Thiswillworkformostintents, butsomekeywordscontainsubschemas. Forthese, wedon't want to hold a subschema because, as mentioned before, they can'tbeedited. Instead, we'll hold a context object that represents the subschema: its type, attribute set, and the intents required to build it. For these intents, we *also* want to implement `IContextContainer`. Here'sthe `ItemsIntent`:
`Replace()` replacesacontextwithagivenhashcodewithanewcontext. Thisiscalled when the system is creating `$ref` intents that point to the new `$defs` intent it's building and distributing them throughout the context tree. Once all the `$ref`s are distributed, the system will add the `$defs` intent to the root context to be applied at the last step.
The other source for intents are attributes. These are handled once the generator has completed adding the intents it needs to.
@@ -483,6 +477,13 @@ The occasion may arise where you want to handle an attribute that's defined in s
483
477
> The confusing bit is that these also have a `params` overload that appears to just take `ISchemaKeywordIntent[]`. However, it works the same as the non-`params` overload in that each array represents a subschema.
484
478
{: .prompt-warning }
485
479
480
+
#### Generic parameter support
481
+
482
+
In order for an attribute to be applicable to a generic parameter (as mentioned [above](#schema-schemagen-best-practices)), your attribute will need to implement the `INestableAttribute` interface, which adds a `GenericParameter` property.
483
+
484
+
> This property MUST default to -1. The index is zero-based, so a value of -1 indicates the root type.
485
+
{: .prompt-warning }
486
+
486
487
### Refiners {#schema-schemagen-refiners}
487
488
488
489
Sometimes you may need to make minor adjustments to the generated schemas dynamically. For this you'll need to create an implementation of `ISchemaRefiner`.
0 commit comments