1. Wprowadzenie
Tak więc, potrafisz już zamieniać obiekty na JSON (i z powrotem). Ale co zrobić, jeśli do twojego programu trafi coś… dziwnego? Na przykład spodziewałeś się takiego JSONa:
{
"id": 123,
"name": "Alice",
"email": "alice@example.com"
}
A otrzymałeś:
{
"name": 42,
"id": "not a number"
}
Tak, serializatory C# uczciwie spróbują zdeserializować ten chaos, ale często prowadzi to do błędów w czasie wykonania, utraty danych albo nawet do katastrofy biznesowej. Zanim wyślesz dane dalej, zapiszesz je do bazy albo prześlesz przez sieć, sprawdź, że dane są poprawne — inaczej twój kod będzie jak akrobata bez asekuracji.
Dlatego potrzebna jest walidacja: to automatyczne sprawdzenie, że JSON spełnia zasady — typy wartości, obowiązkowość pól, zakresy, strukturę itd.
2. Jakie są sposoby walidacji JSON?
W C# i .NET są trzy główne podejścia:
- Walidacja własnym kodem: ręcznie parsujemy obiekt, piszemy sprawdzenia i rzucamy wyjątki w razie błędów.
- Atrybuty walidacyjne na modelach: np. [Required], [Range], [EmailAddress] z przestrzeni nazw System.ComponentModel.DataAnnotations.
- Walidacja za pomocą JSON Schema — nasz dzisiejszy bohater!
JSON Schema to standard pozwalający formalnie opisać, jak powinien wyglądać poprawny dokument. Schemat to też JSON. Określasz, jakie pola są potrzebne, jakich typów, jakie wartości są dozwolone itd.
Za pomocą schematów opisujesz:
- Które pola muszą występować.
- Jakie typy są oczekiwane (string, array, number, object…).
- Które pola są wymagane, a które nie.
- Zakresy wartości (np. wiek od 0 do 150).
- Maski, długości, listy wartości itd.
Przykład najprostszego schematu
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string" }
},
"required": ["id", "name"]
}
3. Podstawowe elementy JSON Schema
Przeanalizujmy na przykładach, co znaczą podstawowe klucze w JSON Schema.
| Klucz | Opis | Przykład |
|---|---|---|
|
Link do wersji standardu JSON Schema | |
|
Typ wartości (object, array, string, number itd.) | |
|
Opis właściwości obiektu | |
|
Tablica wymaganych pól | |
|
Opis typu elementów w tablicy | |
|
Lista dozwolonych wartości | |
, |
Ograniczenia dla liczb | |
, |
Ograniczenia długości stringów | |
|
Wyrażenie regularne dla stringów | |
Wizualnie: przykład schematu dla tablicy ludzi
{
"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. Jak przeprowadzić walidację JSON względem schematu w C#
W .NET brak standardowego wsparcia dla JSON Schema (stan na .NET 9). W praktyce używa się bibliotek zewnętrznych: NJsonSchema lub Newtonsoft.Json.Schema (Json.NET Schema).
Instalacja pakietu Newtonsoft.Json.Schema (Json.NET Schema)
Wykonaj w terminalu (w folderze projektu):
dotnet add package Newtonsoft.Json.Schema
Ważne: Newtonsoft.Json.Schema — biblioteka komercyjna (dla celów niekomercyjnych dostępna za darmo). Dla projektów hobbystycznych jest idealna.
Przykład sprawdzenia JSON względem schematu
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']
}";
// Załóżmy, że mamy taki JSON
string json = @"{
'id': 123,
'name': 'Alice',
'email': 'alice@example.com'
}";
// Najpierw parsujemy schemat
JSchema schema = JSchema.Parse(schemaJson);
// Parsujemy sam JSON do JToken
JToken jsonObj = JToken.Parse(json);
// Sprawdzamy, czy JSON pasuje do schematu
bool valid = jsonObj.IsValid(schema, out IList<string> errors);
if (valid)
{
Console.WriteLine("JSON jest walidny!");
}
else
{
Console.WriteLine("JSON NIE jest walidny!");
foreach (var error in errors)
Console.WriteLine(error);
}
Jak używać walidacji JSON w aplikacji?
Zwykle: najpierw walidacja (IsValid), potem deserializacja (JsonConvert.DeserializeObject<T>) i dopiero później — logika biznesowa. To wycina śmieciowe dane na wczesnym etapie.
if (jsonObj.IsValid(schema))
{
// Wszystko dobrze, można deserializować
var person = JsonConvert.DeserializeObject<Person>(json);
}
else
{
// Zatrzymać przetwarzanie! Dane niepoprawne.
}
5. Zastosowanie JSON Schema w praktyce
Kiedy potrzebna walidacja?
- Przy otrzymywaniu danych przez API (zwłaszcza od zewnętrznych systemów i różnych klientów).
- Przy migracji danych między mikroserwisami i bazami.
- Przy generowaniu formularzy UI i autopowielaniu, jeśli formularz budowany jest na podstawie schematu.
- Na rozmowach kwalifikacyjnych: pytanie „co jeśli przyszedł nieprawidłowy JSON?” pojawia się często.
Ciekawostka dla ciekawskich
- Sprawdzanie formatów: "format": "email", "date-time".
- Złożone reguły: anyOf, oneOf, allOf.
- Walidacja zagnieżdżonych obiektów i tablic.
- Rozszerzenia schematów pod własne potrzeby.
6. Duży praktyczny przykład
Dane wejściowe (lista użytkowników):
[
{ "id": 1, "name": "Alice", "email": "alice@example.com" },
{ "id": 2, "name": "Bob" },
{ "id": "what is this?", "name": 123, "email": "not-an-email" }
]
Schemat:
{
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string", "minLength": 2, "maxLength": 50 },
"email": { "type": "string", "format": "email" }
},
"required": ["id", "name"]
}
}
Sprawdzenie w .NET:
string jsonArray = @"[ ... ]"; // patrz wyżej
string schemaJson = @"{ ... }"; // patrz wyżej
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);
// Przykłady komunikatów:
// "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. Typowe błędy przy pracy z JSON Schema
Błąd nr 1: niezgodność schematu z aktualnymi danymi. Po zmianie modelu zapominają zaktualizować schemat. Walidacja albo przepuszcza błędy, albo blokuje poprawne dane.
Błąd nr 2: rozbieżność typów w schemacie i w modelu. Pole w modelu zmieniło typ (np. id stało się stringiem), a w schemacie został integer — validator zacznie się skarżyć.
Błąd nr 3: brak pól wymaganych przez biznes. Nawet jeśli pole nie jest oznaczone jako wymagane, logika może na nim polegać — brak spowoduje awarie.
Błąd nr 4: nieprawidłowy format danych. Pole typu string z emailem lub datą wygląda „jak string”, ale może być niepoprawne. Używaj format i/lub dodatkowych sprawdzeń.
Błąd nr 5: implementacja biblioteki zostaje w tyle za standardem. Standard się rozwija, a biblioteki — nie zawsze nadążają. Niektóre sprawdzenia mogą być nieobecne lub działać inaczej.
GO TO FULL VERSION