LINQ to XML: čtení a dotazy
using System.Xml.Linq;
var doc = XDocument.Load("objednavka.xml"); // nebo XDocument.Parse(text)
var root = doc.Root!;
string cislo = (string)root.Attribute("cislo")!; // "2026-117"
string zakaznik = (string)root.Element("zakaznik")!;
// Dotaz LINQ: položky s více kusy, seřazené podle ceny
var drahe = root.Elements("polozka")
.Where(p => (int)p.Attribute("mnozstvi")! > 1)
.OrderByDescending(p => (decimal)p.Attribute("cena")!)
.Select(p => new { Kod = (string)p.Attribute("kod")!, Nazev = p.Value });
decimal celkem = root.Elements("polozka")
.Sum(p => (int)p.Attribute("mnozstvi")! * (decimal)p.Attribute("cena")!); Elements() vrací přímé potomky, Descendants() všechny potomky v
libovolné hloubce. Hodnoty čti explicitním přetypováním, ne přes
decimal.Parse(x.Value):
// Explicitní přetypování XElement/XAttribute převádí podle pravidel XML Schema,
// tedy nezávisle na jazyku systému: "149.90" je vždy 149,90 (tečka jako oddělovač).
decimal cena = (decimal)polozka.Attribute("cena")!;
// Přetypování na nullable typ vrátí null, když atribut chybí — bez výjimky
int? sleva = (int?)polozka.Attribute("sleva"); XDocument, XmlReader, nebo XmlDocument?
Ukázky pracují s touto objednávkou:
<?xml version="1.0" encoding="UTF-8"?>
<objednavka cislo="2026-117">
<zakaznik>Jana Nováková</zakaznik>
<polozka kod="HR-01" mnozstvi="2" cena="149.90">Hrnek</polozka>
<polozka kod="TA-07" mnozstvi="1" cena="219.00">Talíř</polozka>
</objednavka> | XDocument (LINQ to XML) | XmlReader | XmlDocument | |
|---|---|---|---|
| Styl práce | dotazy LINQ nad stromem | kurzor vpřed, uzel po uzlu | klasický DOM (starší API) |
| Paměť | celý dokument | jen aktuální uzel | celý dokument, víc než XDocument |
| Úpravy a tvorba | pohodlné, funkční konstrukce | jen čtení (zápis XmlWriter) | ano |
| XPath | přes System.Xml.XPath | ne | SelectNodes, SelectSingleNode |
| Kdy použít | většina běžné práce | soubory ve stovkách MB | starší kód, který ho už používá |
Vytvoření XML
XNamespace f = "http://example.cz/faktura";
var doc = new XDocument(
new XDeclaration("1.0", "utf-8", null),
new XElement(f + "faktura",
new XAttribute(XNamespace.Xmlns + "f", f),
new XElement(f + "cislo", "FV2026-042"),
new XElement(f + "odberatel", "Petr & syn"), // & se zapíše jako &
polozky.Select(p => new XElement(f + "polozka",
new XAttribute("kod", p.Kod), p.Nazev))));
doc.Save("faktura.xml"); Serializace XML
Při převodu XML stromu na text (serializaci) knihovna zapíše zvláštní znaky podle pravidel XML, například ampersand jako &. Zápis zpět nemusí zachovat původní prefixy, odsazení, CDATA nebo pořadí atributů; testuj obsah a požadované kódování.
string text = doc.ToString(SaveOptions.DisableFormatting); DOM
XDocument je LINQ to XML, XmlDocument je klasický DOM. Obě reprezentace načítají strom do paměti. XName porovnává URI prostoru i lokální název; XmlDocument nabízí uzly a XPath přes XmlNamespaceManager.
Velké soubory: XmlReader
using System.Xml;
using System.Xml.Linq;
var settings = new XmlReaderSettings { IgnoreWhitespace = true, DtdProcessing = DtdProcessing.Prohibit };
using var reader = XmlReader.Create("feed.xml", settings);
reader.MoveToContent();
while (reader.ReadToFollowing("SHOPITEM"))
{
// Jeden záznam jako XElement — v paměti je vždy jen on
var item = (XElement)XNode.ReadFrom(reader);
Uloz((string)item.Element("PRODUCTNAME")!, (decimal)item.Element("PRICE_VAT")!);
}
Kombinace ReadToFollowing a XNode.ReadFrom spojuje nízkou spotřebu
paměti proudového čtení s pohodlím LINQ pro každý jednotlivý záznam.
XPath
using System.Xml.XPath;
var polozky = doc.XPathSelectElements("//polozka[@mnozstvi > 1]");
double pocet = (double)doc.XPathEvaluate("count(//polozka)");
// Se jmenným prostorem
var nsm = new XmlNamespaceManager(new NameTable());
nsm.AddNamespace("fa", "http://example.cz/faktura");
var cislo = doc.XPathSelectElement("//fa:cislo", nsm); .NET implementuje XPath 1.0. Na složitější podmínky je obvykle čitelnější LINQ; XPath se hodí, když výraz přichází z konfigurace nebo ho sdílíš s jiným jazykem.
Jmenné prostory
XNamespace f = "http://example.cz/faktura";
var doc = XDocument.Parse(
"""<f:faktura xmlns:f="http://example.cz/faktura"><f:cislo>FV2026-042</f:cislo></f:faktura>""");
string cislo = (string)doc.Root!.Element(f + "cislo")!; // "FV2026-042"
// doc.Root.Element("cislo") by vrátilo null — jméno bez jmenného prostoru je jiné jméno
Jméno prvku v LINQ to XML je dvojice jmenný prostor + lokální jméno (XName).
Prefix z dokumentu nehraje roli — záleží jen na URI.
Validace proti XSD
using System.Xml.Schema;
var schemas = new XmlSchemaSet();
schemas.Add(null, "objednavka.xsd");
var doc = XDocument.Load("objednavka.xml", LoadOptions.SetLineInfo);
doc.Validate(schemas, (sender, e) =>
{
var line = (sender as IXmlLineInfo)?.LineNumber;
Console.WriteLine($"{e.Severity} na řádku {line}: {e.Message}");
}); Bezpečnost: XmlReader a XDocument jsou v moderním
.NET ve výchozím nastavení chráněné proti XXE. U
XmlDocument ve starším kódu nastav XmlResolver = null a u cizích
dat DtdProcessing = DtdProcessing.Prohibit.
Bezpečné nastavení parseru
Vytvoř XmlReader s DtdProcessing.Prohibit, XmlResolver = null a limitem MaxCharactersInDocument. Takový reader předávej také do XDocument.Load a serializátoru. Nastavení se musí uplatnit na skutečně používané cestě čtení; příklad a limity viz XXE.
Mapování na třídy: XmlSerializer
using System.Xml.Serialization;
[XmlRoot("objednavka")]
public class Objednavka
{
[XmlAttribute("cislo")] public string Cislo { get; set; } = "";
[XmlElement("zakaznik")] public string Zakaznik { get; set; } = "";
[XmlElement("polozka")] public List<Polozka> Polozky { get; set; } = new();
}
public class Polozka
{
[XmlAttribute("kod")] public string Kod { get; set; } = "";
[XmlAttribute("mnozstvi")] public int Mnozstvi { get; set; }
[XmlText] public string Nazev { get; set; } = "";
}
var serializer = new XmlSerializer(typeof(Objednavka));
using var stream = File.OpenRead("objednavka.xml");
var objednavka = (Objednavka)serializer.Deserialize(stream)!;
Třídy pro složitá schémata nemusíš psát ručně: nástroj xsd.exe (nebo
dotnet-xscgen) je vygeneruje z XSD.
Načtení JSON
JSON parser vrací datovou strukturu, ne XML strom. Chyby syntaxe zpracuj jako chyby vstupu; data ověř také strukturálně.
using System.Text.Json;
using var data = JsonDocument.Parse("{\"jmeno\":\"Jana\",\"vek\":20}");
Console.WriteLine(data.RootElement.GetProperty("jmeno").GetString()); Vytvoření JSON
Vytvoř hodnotu v paměti a nech její textový zápis na knihovně.
var data = new { jmeno = "Jana", vek = 20, aktivni = true }; Serializace JSON
Neskládej text ručně. Ověř přesnost čísel, reprezentaci dat a chování nečíselných hodnot.
string text = JsonSerializer.Serialize(data, new JsonSerializerOptions { WriteIndented = true }); JSON Schema
Standardní knihovna neposkytuje plnou validaci JSON Schema. Vyber knihovnu s podporou požadované verze pravidel JSON Schema z oficiální dokumentace či katalogu. .NET může použít JsonSchema.Net. Základy a rozdíly verzí vysvětluje kurz JSON Schema.
Dokumentace API
Příklady JSON vycházejí z oficiální dokumentace jazyka. Překlady typů, maximální hloubka a chování při chybě jsou vlastnosti knihovny, ne samotné syntaxe JSON.
JSON Schema v JsonSchema.Net
using System.Text.Json;
using Json.Schema;
using var schemaText = JsonDocument.Parse("""
{"$schema":"https://json-schema.org/draft/2020-12/schema",
"type":"object","required":["id"],
"properties":{"id":{"type":"string"}},"additionalProperties":false}
""");
var schema = JsonSchema.Build(schemaText.RootElement,
new BuildOptions { Dialect = Dialect.Draft202012 });
foreach (var text in new[] {"{\"id\":\"001\"}", "{\"id\":1}"})
{
using var data = JsonDocument.Parse(text);
var result = schema.Evaluate(data.RootElement);
Console.WriteLine(result.IsValid);
} Založ konzolový projekt dotnet new console -n SchemaDemo, v něm spusť dotnet add package JsonSchema.Net --version 9.4.0, nahraď Program.cs ukázkou a spusť dotnet run. Výstup je True a False. Příklad používá novější build/evaluate API a výslovně dialekt 2020-12; starší návody s FromText mohou patřit k jiné hlavní verzi knihovny.
JsonDocument/JsonElement dávají strom hodnot. Nejsou XDocument a nelze na ně použít XML XPath. Po validaci můžeš deserializovat do konkrétní třídy System.Text.Json, ale typovaná deserializace sama neověří všechna JSON Schema pravidla. Meta schéma, registrace externích schémat a formáty musí odpovídat konfiguraci validátoru. Zdroj API: JsonSchema.Net Basics.
Newtonsoft.Json a System.Text.Json
System.Text.Json je součástí .NET od verze 3.0 a používá ho ASP.NET Core. Newtonsoft.Json (Json.NET) je samostatný balíček NuGet, který mnoho projektů používá dál. Microsoft výslovně uvádí, že System.Text.Json se zaměřuje na výkon, bezpečnost a shodu se standardem a nemá cíl dosáhnout stejné sady funkcí. Migrace proto není povinná. Vyplatí se u nového kódu, u ASP.NET Core a tam, kde chceš méně závislostí nebo source generator.
| Newtonsoft.Json | System.Text.Json |
|---|---|
| [JsonProperty("id")] | [JsonPropertyName("id")] |
| [JsonProperty(Required = Required.Always)] | [JsonRequired] nebo modifikátor required |
| [JsonIgnore], NullValueHandling.Ignore | [JsonIgnore], [JsonIgnore(Condition = WhenWritingNull)] |
| [JsonConverter(typeof(…))] | [JsonConverter(typeof(…))] — jiné rozhraní konvertoru |
| [JsonExtensionData] | [JsonExtensionData] |
| MissingMemberHandling.Error | [JsonUnmappedMemberHandling(Disallow)] (.NET 8+) |
| TypeNameHandling | [JsonPolymorphic] a [JsonDerivedType] s výslovným seznamem typů (.NET 7+) |
| ReferenceLoopHandling.Ignore | ReferenceHandler.IgnoreCycles (cyklus zapíše jako null) |
| JObject, JToken | JsonNode (měnitelný), JsonDocument / JsonElement (jen čtení) |
| JsonConvert.PopulateObject | není; jen JsonObjectCreationHandling.Populate pro vlastnosti (.NET 8+) |
Největší rozdíly nejsou v názvech atributů, ale ve výchozím chování. Kód po přepsání atributů zkompiluje, a přesto čte jiná data:
- Velikost písmen. Newtonsoft páruje názvy vlastností bez ohledu na velikost písmen, System.Text.Json přesně. ASP.NET Core používá
JsonSerializerDefaults.Web(camelCase, bez ohledu na velikost písmen, čísla i v uvozovkách), samostatnýJsonSerializerne. - Přísnější syntaxe. Komentáře, koncové čárky a apostrofy System.Text.Json odmítne, protože je RFC 8259 nedovoluje. Povolit je lze volbami
ReadCommentHandlingaAllowTrailingCommas. - Escapování. System.Text.Json ve výchozím stavu escapuje všechny znaky mimo ASCII a znaky citlivé v HTML. Výsledek je platný a rovnocenný JSON, jen hůř čitelný.
- Polymorfismus.
TypeNameHandlingv Newtonsoftu nastavené na jinou hodnotu nežNonenačte typ uvedený ve vstupu, což je u nedůvěryhodných dat známé riziko. System.Text.Json přijme jen typy, které vyjmenuješ v[JsonDerivedType].
public class Volna { public string? Id { get; set; } }
var vstup = """{"ID":"k2"}""";
JsonSerializer.Deserialize<Volna>(vstup)!.Id; // null
JsonSerializer.Deserialize<Volna>(vstup,
new JsonSerializerOptions(JsonSerializerDefaults.Web))!.Id; // "k2"
JsonSerializer.Deserialize<Kniha>("""{"id":"k3","autori":[],"admin":true}""");
// JsonException: The JSON property 'admin' could not be mapped to any .NET member
// contained in type 'Kniha'.
JsonSerializer.Deserialize<Kniha>("""{"nazev":"X","autori":[]}""");
// JsonException: JSON deserialization for type 'Kniha' was missing required
// properties including: 'id'. Jedna třída pro JSON i XML
Atributy System.Text.Json a XmlSerializer se navzájem neovlivňují, takže jedna třída může mít mapování pro oba formáty. Každý serializer čte jen svoje atributy:
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Unicode;
using System.Xml.Serialization;
var kniha = new Kniha { Id = "k1", Nazev = "Žluťoučký kůň", Cena = 199.90m, Autori = ["Jana", "Petr"] };
Console.WriteLine(JsonSerializer.Serialize(kniha));
var citelne = new JsonSerializerOptions { Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) };
Console.WriteLine(JsonSerializer.Serialize(kniha, citelne));
var xml = new XmlSerializer(typeof(Kniha));
var ns = new XmlSerializerNamespaces(); ns.Add("", ""); // bez xmlns:xsi a xmlns:xsd
using var w = new StringWriter();
xml.Serialize(w, kniha, ns);
Console.WriteLine(w);
[XmlRoot("kniha")]
[JsonUnmappedMemberHandling(JsonUnmappedMemberHandling.Disallow)]
public class Kniha
{
[JsonPropertyName("id"), XmlAttribute("id")]
public required string Id { get; set; }
[JsonPropertyName("nazev"), XmlElement("nazev")]
public string Nazev { get; set; } = "";
[JsonPropertyName("cena"), XmlElement("cena")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public decimal? Cena { get; set; }
[JsonPropertyName("autori"), XmlArray("autori"), XmlArrayItem("autor")]
public List<string> Autori { get; set; } = [];
} {"id":"k1","nazev":"\u017Dlu\u0165ou\u010Dk\u00FD k\u016F\u0148","cena":199.90,"autori":["Jana","Petr"]}
{"id":"k1","nazev":"Žluťoučký kůň","cena":199.90,"autori":["Jana","Petr"]}
<?xml version="1.0" encoding="utf-16"?>
<kniha id="k1">
<nazev>Žluťoučký kůň</nazev>
<cena>199.90</cena>
<autori>
<autor>Jana</autor>
<autor>Petr</autor>
</autori>
</kniha> - Diakritika. První řádek je výchozí výstup s
Žmísto „Ž“. Druhý vznikl sJavaScriptEncoder.Create(UnicodeRanges.All). Ten dál escapuje znaky citlivé v HTML;UnsafeRelaxedJsonEscapingje nepoužívej, pokud JSON vkládáš do HTML stránky. - encoding="utf-16".
StringWriterje řetězec v .NET, tedy UTF-16, a deklarace to tak uvádí. Pro soubor nebo HTTP použijXmlWriter.Create(stream, new XmlWriterSettings { Encoding = new UTF8Encoding(false) }), jinak se skutečné a deklarované kódování rozejdou. - Kolekce. V JSON je seznam pole, v XML obalový element
autoris opakovanýmautor. BezXmlArrayaXmlArrayItemby se jmenovaly podle vlastnosti a typu (Autori,string). - Povinnost a neznámá pole platí jen pro JSON.
XmlSerializerneznámé elementy tiše přeskočí a modifikátorrequirednekontroluje. Pro XML použij validaci proti XSD.
Pro rychlejší start a nasazení s Native AOT vygeneruje System.Text.Json serializační kód při kompilaci:
[JsonSerializable(typeof(Kniha))]
public partial class KnihyJson : JsonSerializerContext { }
string text = JsonSerializer.Serialize(kniha, KnihyJson.Default.Kniha);