Skip to content
Matthew Adams By Matthew Adams Co-Founder
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

Example code

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;

Matthew Adams

Co-Founder

Matthew Adams

Matthew was CTO of a venture-backed technology start-up in the UK & US for 10 years, and is now the co-founder of endjin, which provides technology strategy, experience and development services to its customers who are seeking to take advantage of Microsoft Azure and the Cloud.