1. Introduction
So, you already know how to turn objects into JSON (and back). But what if something... weird arrives into your program? For example, you expected this JSON:
{
"id": 123,
"name": "Alice",
"email": "alice@example.com"
}
But you got:
{
"name": 42,
"id": "not a number"
}
Yes, C# serializers will honestly try to deserialize this mess, but more often than not this leads to runtime errors, data loss, or even broken business logic. Before you pass data further, save it to a database, or send it over the network, check that the data is valid — otherwise your code is like an acrobat without a safety net.
That's why validation exists: it's an automatic check that the JSON matches rules — value types, required fields, ranges, structure, etc.
2. What ways to validate JSON exist?
In C# and .NET there are three main approaches:
- Validation with custom code: manually parse the object, write checks and throw exceptions on errors.
- Validation attributes on models: e.g. [Required], [Range], [EmailAddress] from the namespace System.ComponentModel.DataAnnotations.
- Validation using JSON Schema — our hero for today!
JSON Schema is a standard that lets you formally describe what a valid document should look like. A schema is also JSON. You declare which fields are needed, what types they should be, which values are allowed, etc.
With schemas you describe:
- Which fields must be present.
- Which types are expected (string, array, number, object...).
- Which fields are required and which are optional.
- Value ranges (for example, age from 0 to 150).
- Patterns, lengths, enumerations of values, and more.
Example of the simplest schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string" }
},
"required": ["id", "name"]
}
3. Main elements of JSON Schema
Let's figure out with examples what the main keys in JSON Schema mean.
| Key | Description | Example |
|---|---|---|
|
Reference to the version of the JSON Schema standard | |
|
Type of the value (object, array, string, number, etc.) | |
|
Description of an object's properties | |
|
Array of required fields | |
|
Description of the type of array elements | |
|
List of allowed values | |
, |
Constraints for numbers | |
, |
String length constraints | |
|
Regular expression for strings | |
Visually: example schema for an array of people
{
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string", "minLength": 2, "maxLength": 50 },
"email": { "type": "string", "format": "email" }
},
"required": ["id", "name"]
}
}
4. How to validate JSON against a schema in C#
There is no built-in JSON Schema support in .NET (as of .NET 9). In practice people use third-party libraries: NJsonSchema or Newtonsoft.Json.Schema (Json.NET Schema).
Install the Newtonsoft.Json.Schema (Json.NET Schema) package
Run in the terminal (in your project folder):
dotnet add package Newtonsoft.Json.Schema
Important: Newtonsoft.Json.Schema is a commercial library (free for non-commercial use). It's perfect for pet projects.
Example of checking JSON against a schema
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Schema;
string schemaJson = @"{
'type': 'object',
'properties': {
'id': { 'type': 'integer' },
'name': { 'type': 'string' },
'email': { 'type': 'string', 'format': 'email' }
},
'required': ['id', 'name']
}";
// Imagine we have this JSON
string json = @"{
'id': 123,
'name': 'Alice',
'email': 'alice@example.com'
}";
// First parse the schema
JSchema schema = JSchema.Parse(schemaJson);
// Parse the JSON into a JToken
JToken jsonObj = JToken.Parse(json);
// Check if JSON matches the schema
bool valid = jsonObj.IsValid(schema, out IList<string> errors);
if (valid)
{
Console.WriteLine("JSON is valid!");
}
else
{
Console.WriteLine("JSON is NOT valid!");
foreach (var error in errors)
Console.WriteLine(error);
}
How to use JSON validation in an app?
Usually: validate first (IsValid), then deserialize (JsonConvert.DeserializeObject<T>) and only after that — business logic. This filters out garbage data early.
if (jsonObj.IsValid(schema))
{
// All good, safe to deserialize
var person = JsonConvert.DeserializeObject<Person>(json);
}
else
{
// Stop processing! Data is invalid.
}
5. Applying JSON Schema in real life
When validation is needed?
- When receiving data via API (especially from external systems and varied clients).
- When migrating data between microservices and databases.
- When generating UI forms and autofill if the form is built from a schema.
- In interviews: the question "what if a wrong JSON arrives?" comes up often.
Interesting note for the curious
- Format checks: "format": "email", "date-time".
- Composite rules: anyOf, oneOf, allOf.
- Validation of nested objects and arrays.
- Extending schemas for your own needs.
6. Large practical example
Input data (a list of users):
[
{ "id": 1, "name": "Alice", "email": "alice@example.com" },
{ "id": 2, "name": "Bob" },
{ "id": "what is this?", "name": 123, "email": "not-an-email" }
]
Schema:
{
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string", "minLength": 2, "maxLength": 50 },
"email": { "type": "string", "format": "email" }
},
"required": ["id", "name"]
}
}
Check in .NET:
string jsonArray = @"[ ... ]"; // see above
string schemaJson = @"{ ... }"; // see above
JSchema schema = JSchema.Parse(schemaJson);
JToken arrayToken = JToken.Parse(jsonArray);
bool isValid = arrayToken.IsValid(schema, out IList<string> errs);
if (!isValid)
{
foreach (var error in errs)
Console.WriteLine(error);
// Example messages:
// "String 'what is this?' is not a valid integer."
// "Integer 123 is not a valid string."
// "String 'not-an-email' is not a valid email address."
}
7. Common mistakes when working with JSON Schemas
Mistake #1: schema doesn't match current data. After changing the model people forget to update the schema. Validation either lets errors through or blocks valid data.
Mistake #2: mismatched types between schema and model. A field in the model changed type (for example, id became a string) but the schema still has integer — the validator will start complaining.
Mistake #3: missing business-required fields. Even if a field isn't marked required, your logic might rely on it — its absence will cause failures.
Mistake #4: wrong data format. A string field for email or date looks like a string but can be invalid. Use format and/or extra checks.
Mistake #5: the library lags behind the standard. The standard evolves, and libraries don't always keep up. Some checks may be missing or behave differently.
GO TO FULL VERSION