Json Schema Patterns in .NET - Creating tuples
In this series we are cataloging common patterns with JSON Schema and the .NET code generated by Corvus.JsonSchema.
It is especially useful for developers who want to move to a schema-first approach, but are unsure how that will map to their .NET code.
We are focusing on draft 2020-12 and the dotnet8.0 code generation, but very similar patterns apply for older versions of both .NET and draft 2019-09, draft 7, and even draft 6. We will highlight the key differences as we go.
If you have no experience of JSON Schema at all, I would recommend you read the getting started step-by-step documentation provided by the JSON Schema team.
Using prefixItems
and unevaluatedItems
to create tuples.
.NET has the concept of a ValueTuple<T1,...TN>
- a lightweight type that can represent a small, strongly typed collection of values.
We typically encounter it through the special C# syntax available to define and instantiate them.
(int, string, bool) value = (3, "hello", true);
Console.WriteLine($"{value.Item1}, {value.Item2}, {value.Item3});
JSON Schema allows us to define an array whose items are constrained to a specific ordered list of schema.
We do this with the prefixItems
keyword.
To ensure that no other items are permitted than those in the ordered list, we also add unevaluatedItems: false
.
This is another area of divergence between draft 2020-12 and prior drafts. In those versions, you should use the array form of items
and additionalItems
.
Notice that a tuple is in effect a closed type - you cannot add additional items to it. Just as unevaluatedProperties: false
closes an object
type, unevaluatedItems: false
closes an array type.
File: three-tuple.json
{
"title": "A tuple of int32, string, boolean",
"type": "array",
"prefixItems": [
{
"type": "integer",
"format": "int32"
},
{
"type": "string"
},
{
"type": "boolean"
}
],
"unevaluatedItems": false
}
Generate the code:
generatejsonschematypes --outputPath Model --rootNamespace JsonSchemaSample.Api three-tuple.json
Generating: ThreeTuple
using Corvus.Json;
using JsonSchemaSample.Api;
// Create a tuple from a .NET tuple
(int, string, bool) dotnetTuple = (3, "Hello", false);
ThreeTuple threeTuple = dotnetTuple;
// Create a tuple directly
ThreeTuple threeTuple2 = ThreeTuple.Create(3, "Hello", false);
// Parse a JSON array
string threeTupleJson =
"""
[3, "Hello", false]
""";
using ParsedValue<ThreeTuple> threeTuple3 = ParsedValue<ThreeTuple>.Parse(threeTupleJson);
if (threeTuple3.Instance == threeTuple)
{
// The tuples are equal
Console.WriteLine("The tuples are equal");
}
else
{
Console.WriteLine("The tuples are not equal");
}
// Access the item values
Console.WriteLine($"{threeTuple.Item1}, {threeTuple.Item2}, {threeTuple.Item3}");
// Convert to a .NET tuple
(JsonInt32, JsonString, JsonBoolean) tupleFromThreeTuple = threeTuple;
// Automatically convert to a tuple where implicit conversions are available
(int, JsonString, bool) dotnetTupleFromThreeTuple = threeTuple;