Skip to content

Commit 0ff2410

Browse files
CopilotBillWagner
andcommitted
Add XML roundtripping section with carriage return entity documentation
Co-authored-by: BillWagner <[email protected]>
1 parent ca7e4d5 commit 0ff2410

File tree

1 file changed

+78
-0
lines changed

1 file changed

+78
-0
lines changed

docs/standard/linq/preserve-white-space-serializing.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,81 @@ The following methods in the <xref:System.Xml.Linq.XElement> and <xref:System.Xm
2525
If the method doesn't take <xref:System.Xml.Linq.SaveOptions> as an argument, then the method will format (indent) the serialized XML. In this case, all insignificant white space in the XML tree is discarded.
2626

2727
If the method does take <xref:System.Xml.Linq.SaveOptions> as an argument, then you can specify that the method not format (indent) the serialized XML. In this case, all white space in the XML tree is preserved.
28+
29+
## Roundtripping XML with carriage return entities
30+
31+
The whitespace preservation discussed in this article is different from XML roundtripping. When XML contains carriage return entities (`&#xD;`), LINQ to XML's standard serialization might not preserve them in a way that allows perfect roundtripping.
32+
33+
Consider the following example XML that contains carriage return entities:
34+
35+
```xml
36+
<x xml:space="preserve">a&#xD;
37+
b
38+
c&#xD;</x>
39+
```
40+
41+
When you parse this XML with `XDocument.Parse()`, the root element's value becomes `"a\r\nb\nc\r"`. However, if you reserialize it using LINQ to XML methods, the carriage returns are not entitized:
42+
43+
```csharp
44+
string xmlWithCR = """<x xml:space="preserve">a&#xD;
45+
b
46+
c&#xD;</x>""";
47+
48+
XDocument doc = XDocument.Parse(xmlWithCR);
49+
Console.WriteLine($"Original parsed value: {string.Join("", doc.Root.Value.Select(c => c == '\r' ? "\\r" : c == '\n' ? "\\n" : c.ToString()))}");
50+
// Output: a\r\nb\nc\r
51+
52+
string reserialized = doc.ToString(SaveOptions.DisableFormatting);
53+
Console.WriteLine($"Reserialized XML: {reserialized}");
54+
// Output: <x xml:space="preserve">a
55+
// b
56+
// c</x>
57+
58+
XDocument reparsed = XDocument.Parse(reserialized);
59+
Console.WriteLine($"Reparsed value: {string.Join("", reparsed.Root.Value.Select(c => c == '\r' ? "\\r" : c == '\n' ? "\\n" : c.ToString()))}");
60+
// Output: a\nb\nc\n
61+
```
62+
63+
The values are different: the original was `"a\r\nb\nc\r"` but after roundtripping it becomes `"a\nb\nc\n"`.
64+
65+
### Solution: Use XmlWriter with NewLineHandling.Entitize
66+
67+
To achieve true XML roundtripping that preserves carriage return entities, use <xref:System.Xml.XmlWriter> with <xref:System.Xml.XmlWriterSettings.NewLineHandling> set to <xref:System.Xml.NewLineHandling.Entitize>:
68+
69+
```csharp
70+
string xmlWithCR = """<x xml:space="preserve">a&#xD;
71+
b
72+
c&#xD;</x>""";
73+
74+
XDocument doc = XDocument.Parse(xmlWithCR);
75+
76+
// Create XmlWriter settings with NewLineHandling.Entitize
77+
XmlWriterSettings settings = new XmlWriterSettings
78+
{
79+
NewLineHandling = NewLineHandling.Entitize,
80+
OmitXmlDeclaration = true
81+
};
82+
83+
// Serialize using XmlWriter
84+
using StringWriter stringWriter = new StringWriter();
85+
using (XmlWriter writer = XmlWriter.Create(stringWriter, settings))
86+
{
87+
doc.WriteTo(writer);
88+
}
89+
90+
string roundtrippedXml = stringWriter.ToString();
91+
Console.WriteLine($"Roundtripped XML: {roundtrippedXml}");
92+
// Output: <x xml:space="preserve">a&#xD;
93+
// b
94+
// c&#xD;</x>
95+
96+
// Verify roundtripping preserves the original value
97+
XDocument roundtrippedDoc = XDocument.Parse(roundtrippedXml);
98+
bool valuesMatch = doc.Root.Value == roundtrippedDoc.Root.Value;
99+
Console.WriteLine($"Values match after roundtripping: {valuesMatch}");
100+
// Output: True
101+
```
102+
103+
When you need to preserve carriage return entities for XML roundtripping, use <xref:System.Xml.XmlWriter> with the appropriate <xref:System.Xml.XmlWriterSettings> instead of LINQ to XML's built-in serialization methods.
104+
105+
For more information about <xref:System.Xml.XmlWriter> and its settings, see <xref:System.Xml.XmlWriter?displayProperty=fullName>.

0 commit comments

Comments
 (0)