Condividi tramite


Mantenere lo spazio vuoto durante la serializzazione (LINQ to XML)

Questo articolo descrive come controllare lo spazio vuoto durante la serializzazione di un albero XML.

Uno scenario comune consiste nel leggere XML con rientro, creare un albero XML in memoria senza nodi di testo contenenti spazi bianchi, ovvero senza conservarli, eseguire alcune operazioni sull'XML e quindi salvare l'XML con rientro. Quando si serializza il codice XML con la formattazione, viene mantenuto solo uno spazio vuoto significativo nell'albero XML. Si tratta del comportamento predefinito per LINQ to XML.

Un altro scenario comune consiste nel leggere e modificare il documento XML che è già stato intenzionalmente indentato. È possibile che non si voglia modificare questo rientro in alcun modo. A tale scopo, in LINQ to XML si mantiene lo spazio vuoto quando si carica o si analizza il codice XML e si disabilita la formattazione quando si serializza il codice XML.

Comportamento degli spazi vuoti dei metodi che serializzano alberi XML

I metodi seguenti nelle XElement classi e XDocument serializzano un albero XML. È possibile serializzare un albero XML in un file, in un TextReaderoggetto o in un oggetto XmlReader. Il ToString metodo serializza in una stringa.

Se il metodo non accetta SaveOptions come argomento, formatterà (indenterà) il codice XML serializzato. In questo caso, tutti gli spazi vuoti non significativi nell'albero XML vengono eliminati.

Se il metodo accetta SaveOptions come argomento, è possibile specificare che il metodo non debba formattare (rientrare) il codice XML serializzato. In questo caso, tutti gli spazi vuoti nell'albero XML vengono mantenuti.

Roundping XML con entità ritorno a capo

La conservazione degli spazi vuoti descritta in questo articolo è diversa dal round-trip XML. Quando XML contiene entità di ritorno carrello (
), la serializzazione standard di LINQ to XML potrebbe non conservarle in un modo che permetta un round trip perfetto.

Si consideri il codice XML di esempio seguente che contiene entità di ritorno a capo:

<x xml:space="preserve">a&#xD;
b
c&#xD;</x>

Quando si analizza questo codice XML con XDocument.Parse(), il valore dell'elemento radice diventa "a\r\nb\nc\r". Tuttavia, se si riserializzi con i metodi LINQ to XML, i ritorni a capo non sono convertiti in entità.

string xmlWithCR = """
    <x xml:space="preserve">a
    b
    c
</x>
    """;

XDocument doc = XDocument.Parse(xmlWithCR);
Console.WriteLine($"Original parsed value: {string.Join("", doc.Root!.Value.Select(c => c == '\r' ? "\\r" : c == '\n' ? "\\n" : c.ToString()))}");
// Output: a\r\nb\nc\r

string reserialized = doc.ToString(SaveOptions.DisableFormatting);
Console.WriteLine($"Reserialized XML: {reserialized}");
// Output: <x xml:space="preserve">a
// b
// c</x>

XDocument reparsed = XDocument.Parse(reserialized);
Console.WriteLine($"Reparsed value: {string.Join("", reparsed.Root!.Value.Select(c => c == '\r' ? "\\r" : c == '\n' ? "\\n" : c.ToString()))}");
// Output: a\nb\nc\n

I valori sono diversi: l'originale era "a\r\nb\nc\r" ma dopo il round tripping diventa "a\nb\nc\n".

Soluzione: usare XmlWriter con NewLineHandling.Entitize

Per ottenere un vero ciclo completo XML che preserva le entità di ritorno a capo, utilizzare XmlWriter con NewLineHandling impostato su Entitize:

string xmlWithCR = """
    <x xml:space="preserve">a
    b
    c
</x>
    """;

XDocument doc = XDocument.Parse(xmlWithCR);

// Create XmlWriter settings with NewLineHandling.Entitize
XmlWriterSettings settings = new XmlWriterSettings
{
    NewLineHandling = NewLineHandling.Entitize,
    OmitXmlDeclaration = true
};

// Serialize using XmlWriter
using StringWriter stringWriter = new StringWriter();
using (XmlWriter writer = XmlWriter.Create(stringWriter, settings))
{
    doc.WriteTo(writer);
}

string roundtrippedXml = stringWriter.ToString();
Console.WriteLine($"Roundtripped XML: {roundtrippedXml}");
// Output: <x xml:space="preserve">a
// b
// c
</x>

// Verify roundtripping preserves the original value
XDocument roundtrippedDoc = XDocument.Parse(roundtrippedXml);
bool valuesMatch = doc.Root!.Value == roundtrippedDoc.Root!.Value;
Console.WriteLine($"Values match after roundtripping: {valuesMatch}");

Quando è necessario mantenere le entità di ritorni a capo per il round tripping XML, usare XmlWriter con il XmlWriterSettings appropriato anziché i metodi di serializzazione predefiniti di LINQ to XML.

Per ulteriori informazioni su XmlWriter e le relative impostazioni, vedere System.Xml.XmlWriter.