Home Dropping Project Support for Code Generation
Post
Cancel

Dropping Project Support for Code Generation

Some time ago, I released my first attempt at code generation from JSON Schemas. However, I’ve decided to deprecate the library in favor of Corvus.JsonSchema.

When I created JsonSchema.Net.CodeGeneration, I knew about Corvus.JsonSchema, but I thought it was merely an alternative validator. I didn’t truly understand its approach to supporting JSON Schema in .Net.

Today we’re going to take a look at this seeming competitor to see why it actually isn’t one.

What is Corvus.JsonSchema?

Corvus.JsonSchema is a JSON Schema code generator that bakes validation logic directly into the model.

To show this, consider the following schema.

1
2
3
4
5
6
7
8
9
{
  "type": "object",
  "properties": {
    "foo": {
      "type": "integer",
      "minimum": 0
    }
  }
}

As one would expect, the library would generate a class with a single property: int Foo. But it also generates an .IsValid() method that contains all of the validation logic. So if you set model.Foo = -1, the .IsValid() method will return false.

However Corvus.JsonSchema has another trick up its sleeve. But before we get into that, it will help to have some understanding of how System.Text.Json’s JsonElement works.

A quick review of JsonElement

Under the hood, JsonElement captures the portion of the parsed JSON text by using spans. This has a number of follow-on benefits:

  • By avoiding substringing, there are no additional heap allocations.
  • JsonElement can be a struct, which further avoids allocations, because it only maintains references to existing memory.
  • By holding onto the original text, the value can be interpreted different ways. For example, numbers could be read as double or decimal or integer.

As an example, consider this string:

1
{ "foo": 42, "bar": [ "a string", false ] }

Five different JsonElements would be created:

  • top-level object
  • number value under foo
  • array value under bar
  • first element of bar array
  • second element of bar array

But the kicker is that everything simply references the original string.

ValueBacking span
top-level objectstart: 0, length: 44
number value under foostart: 9, length: 2
array value under barstart: 20, length: 21
first element of bar arraystart: 22, length: 10
second element of bar arraystart: 34, length: 5

Back to the validator

Corvus.JsonSchema builds on this “backing data” pattern that JsonElement establishes. Instead of creating a backing field that is the same type that the property exposes, which is the traditional approach for backing fields, the generated code will use a JsonElement for the backing field while the property is still strongly typed.

This means that a model generated by the library can usually be deserialized without any extra allocations, resulting in very high performance!

For a much better explanation of what’s going on inside the package than what I can provide, I recommend you watch their showcase video.

Keep moving forward

Ever since I saw that video, I’ve lamented the fact that it’s only available as a dotnet tool. I’ve always envisioned this functionality as a Roslyn source generator.

To that end, I’ve paired with Matthew Adams, one of the primary contributors to Corvus.JsonSchema, as co-mentor on a project proposal for JSON Schema’s submission to Google’s Summer of Code. This project aims to wrap the existing library in an incremental source generator that uses JSON Schema files within a .Net project to automatically generate models at compile time.

This is a great opportunity to learn about incremental source generators in .Net and build your open source portfolio. If this sounds like a fun project, please make your interest known by commenting on the proposal issue linked above.

(Even if it’s not accepted by GSoc, we’re probably going to do it anyway.)

This post is licensed under CC BY 4.0 by the author.

In Pursuit of Native Code

JSON Logic Without Models