Skip to content
Matthew Adams By Matthew Adams Co-Founder
Json Schema Patterns in .NET - Working with tensors

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.

Defining numeric arrays of fixed size

We have seen how to create arrays of higher rank with fixed size.

The array could contain any item type that you can define in schema. However, our previous example used a numeric type: we constrained it using the type number and the format double.

Here's another example, this time of rank 3.

File: tensor-rank-3.json

{
  "title": "A 4x4x4 tensor of JsonDouble",
  "type": "array",
  "items": { "$ref": "#/$defs/SecondRank" },
  "minItems": 4,
  "maxItems": 4,
  "$defs": {
    "SecondRank": {
      "type": "array",
      "items": { "$ref": "#/$defs/ThirdRank" },
      "minItems": 4,
      "maxItems": 4,
    },
    "ThirdRank": {
      "type": "array",
      "minItems": 4,
      "maxItems": 4,
      "items": { "type": "number", "format": "double" }
    }
  }
}

We can use this in the same way, and also convert it directly to- and from- Span<TNumeric> for use in APIs such as System.Numerics.Tensors.

Generate the code:

generatejsonschematypes --outputPath Model --rootNamespace JsonSchemaSample.Api tensor-rank-3.json
Generating: TensorRank3
Generating: ThirdRank
Generating: SecondRank

Example code

using JsonSchemaSample.Api;

// Initialize an array of rank 3 using collection initialization expressions

TensorRank3 tensor =
    [
        [
            [1.3, 1.4, 9.2, 8.4],
            [2.4, 3.2, 6.3, 7.2],
            [9.4, 6.2, 4.8, 1.4],
            [4.4, 9.4, -3.2, 6.5],
        ],
        [
            [1.3, 1.4, 9.2, 8.4],
            [2.4, 3.2, 6.3, 7.2],
            [9.4, 6.2, 4.8, 1.4],
            [4.4, 9.4, -3.2, 6.5],
        ],
        [
            [1.3, 1.4, 9.2, 8.4],
            [2.4, 3.2, 6.3, 7.2],
            [9.4, 6.2, 4.8, 1.4],
            [4.4, 9.4, -3.2, 6.5],
        ],
        [
            [1.3, 1.4, 9.2, 8.4],
            [2.4, 3.2, 6.3, 7.2],
            [9.4, 6.2, 4.8, 1.4],
            [4.4, 9.4, -3.2, 6.5],
        ]
    ];

// Access an element of a array of rank 3.
double tensorValue = tensor[3][0][1];
// Set the item at the given indices in the array of rank 3
TensorRank3 updatedTensor = tensor.SetItem(3, 0, 1, 3.4);

// Fill a Span<double> with the tensor values.
Span<double> tensorAsSpan = stackalloc double[TensorRank3.ValueBufferSize];
if (updatedTensor.TryGetNumericValues(tensorAsSpan, out int writtenTensor))
{
    bool first = true;

    // Successfully get the tensor as a flat Span<double>
    foreach (double spanValue in tensorAsSpan)
    {
        if (first)
        {
            first = false;
        }
        else
        {
            Console.Write(", ");
        }

        Console.Write(spanValue);
    }

    Console.WriteLine();
}
else
{
    Console.WriteLine("Unable to get values.");
}

// Construct a tensor from a ReadOnlySpan<double>
TensorRank3 fromSpan = TensorRank3.FromValues(tensorAsSpan);

// Find the rank of each array (note that the sub arrays of diminishing rank)
Console.WriteLine($"Rank: {TensorRank3.Rank}, {TensorRank3.SecondRank.Rank}, {TensorRank3.ThirdRank.Rank}");

// Find the dimension (extent) of each particular rank of the array
Console.WriteLine($"Dimension: {TensorRank3.Dimension}, {TensorRank3.SecondRank.Dimension}, {TensorRank3.ThirdRank.Dimension}");

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.