Introducing Corvus.Text.Json V5: Migration, Analyzers, and What's Next
At endjin, we maintain Corvus.JsonSchema, and this is the final post in our series introducing the V5 engine. Over the previous thirteen posts, we've covered pooled parsing, mutable documents, source generators, schema validation, annotations, three query languages, YAML, JSON Patch, extended types, and TOON conversion.
Now let's wrap up with migration from V4 and the production analyzers that ship with V5.
V4 isn't going away
Before we talk about migration, let's be clear: V4 is not deprecated. It continues to be maintained and is the right choice when you want the guarantees of an immutable document model.
| V4 | V5 | |
|---|---|---|
| Mutation | Immutable - With*() returns new instance |
Mutable - Set*() mutates in-place |
| Safety | No aliasing possible | Version-tracked stale detection |
| Performance | Good | Considerably faster |
| Best for | Thread-safe sharing, functional pipelines | High-throughput request/response |
Both engines ship in the same corvusjson CLI tool, share the same schema analysis engine, and support the same JSON Schema drafts. You choose which engine to target with the --engine flag.
Migration path
If you do want to move from V4 to V5, the path is well-supported.
Quick reference
| V4 | V5 |
|---|---|
Corvus.Json namespace |
Corvus.Text.Json namespace |
MyType.Parse(json) |
ParsedJsonDocument<MyType>.Parse(json) |
entity.Validate(ctx, level) |
entity.EvaluateSchema() or entity.EvaluateSchema(collector) |
entity.WithProperty(...) |
mutable.SetProperty(...) via builder |
JsonAny, JsonString, etc. |
JsonElement with GetString(), GetInt32(), etc. |
--engine V4 |
--engine V5 |
Migration analyzers
Install the migration analyzer package:
dotnet add package Corvus.Text.Json.Migration.Analyzers
The analyzers detect V4 patterns - namespace usages, API calls, mutation patterns - and offer automatic Roslyn code fixes. In Visual Studio, you'll see lightbulb suggestions that convert V4 code to the V5 equivalent.
Copilot-assisted migration
For larger migrations, GitHub Copilot can help. The V5 documentation includes a Copilot migration guide with prompts designed to help Copilot understand the V4→V5 mapping and apply it across your codebase.
Production analyzers
V5 ships with 10 Roslyn analyzers that help you write correct, high-performance code:
| ID | What it catches |
|---|---|
| CTJ001 | String literal where UTF-8 u8 suffix would avoid transcoding |
| CTJ002 | Unnecessary cast to .NET type when implicit conversion suffices |
| CTJ003 | Match lambda that should be static to avoid closure allocation |
| CTJ004 | ParsedJsonDocument created without using or Dispose() |
| CTJ005 | JsonWorkspace created without using or Dispose() |
| CTJ006 | JsonDocumentBuilder created without using or Dispose() |
| CTJ007 | EvaluateSchema() result discarded - probably a bug |
| CTJ008 | String comparison where NameEquals would avoid allocation |
| CTJ009 | Manual Utf8JsonWriter creation where workspace renting is available |
| CTJ010 | String-based Parse where ReadOnlyMemory<byte> overload is available |
Six of these have automatic code fixes. CTJ004–CTJ006 are particularly important. They catch the most common mistake with pooled memory: forgetting to dispose.
There's also CTJ-NAV, a refactoring that lets you navigate from a schema-generated type directly to its JSON Schema source in your IDE.
Getting started
# Core library (includes analyzers)
dotnet add package Corvus.Text.Json
# Source generator
dotnet add package Corvus.Text.Json.SourceGenerator
# CLI tool
dotnet tool install --global Corvus.Json.Cli
# Optional: query languages
dotnet add package Corvus.Text.Json.Jsonata
dotnet add package Corvus.Text.Json.JMESPath
dotnet add package Corvus.Text.Json.JsonLogic
# Optional: YAML
dotnet add package Corvus.Text.Json.Yaml
# Optional: JSON Patch
dotnet add package Corvus.Text.Json.Patch
# Optional: dynamic validation
dotnet add package Corvus.Text.Json.Validator
The full documentation is at github.com/corvus-dotnet/Corvus.JsonSchema.
Series recap
Over these fourteen posts, we've covered:
- Why V5 Exists - two engines, one toolchain, different trade-offs
- Source-Generated Types - schema-first types with full IntelliSense
- Schema Validation - over 10× faster, all major drafts
- Pooled-Memory Parsing - 136 bytes per document
- Mutable Documents - builder pattern with version tracking
- Standalone Evaluator - annotations for schema-driven tooling
- JSONata - query and transformation with 100% conformance
- JMESPath - on average 28× faster JSON queries
- JsonLogic - safe business rules as data
- YAML 1.2 - zero-allocation conversion with event streaming
- JSON Patch - RFC 6902 with a fluent builder
- JSON Pointer - zero-allocation path resolution
- Extended Types - UTF-8 URIs, BigNumber, and NodaTime
- Migration, Analyzers, and What's Next (this post)
If you have questions, find bugs, or want to contribute, the GitHub repository is the place to go.