Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>The <code>&amp;#xA;</code> entities are technically called “numeric character references” in XML, and they are resolved when the original document is loaded into the <code>XDocument</code>. This makes your issue problematic to solve, since there is no way of distinguishing resolved whitespace entities from insignificant whitespace (typically used for formatting XML documents for plain-text viewers) after the <code>XDocument</code> has been loaded. Thus, the below only applies if your document does not have any insignificant whitespace.</p> <p>The <code>System.Xml</code> library allows one to preserve whitespace entities by setting the <a href="http://msdn.microsoft.com/en-us/library/system.xml.xmlwritersettings.newlinehandling.aspx"><code>NewLineHandling</code></a> property of the <code>XmlWriterSettings</code> class to <code>Entitize</code>. However, within text nodes, this would only entitize <code>\r</code> to <code>&amp;#xD;</code>, and not <code>\n</code> to <code>&amp;#xA;</code>.</p> <p>The easiest solution is to derive from the <code>XmlWriter</code> class and override its <a href="http://msdn.microsoft.com/en-us/library/system.xml.xmlwriter.writestring.aspx"><code>WriteString</code></a> method to manually replace the whitespace characters with their numeric character entities. The <code>WriteString</code> method also happens to be the place where .NET entitizes characters that are not permitted to appear in text nodes, such as the syntax markers <code>&amp;</code>, <code>&lt;</code>, and <code>&gt;</code>, which are respectively entitized to <code>&amp;amp;</code>, <code>&amp;lt;</code>, and <code>&amp;gt;</code>.</p> <p>Since <code>XmlWriter</code> is abstract, we shall derive from <code>XmlTextWriter</code> in order to avoid having to implement all the abstract methods of the former class. Here is a quick-and-dirty implementation:</p> <pre><code>public class EntitizingXmlWriter : XmlTextWriter { public EntitizingXmlWriter(TextWriter writer) : base(writer) { } public override void WriteString(string text) { foreach (char c in text) { switch (c) { case '\r': case '\n': case '\t': base.WriteCharEntity(c); break; default: base.WriteString(c.ToString()); break; } } } } </code></pre> <p>If intended for use in a production environment, you’d want to do away with the <code>c.ToString()</code> part, since it’s very inefficient. You can optimize the code by batching substrings of the original <code>text</code> that do not contain any of the characters you want to entitize, and feeding them together into a single <code>base.WriteString</code> call.</p> <p>A word of warning: The following naive implementation will not work, since the base <code>WriteString</code> method would replace any <code>&amp;</code> characters with <code>&amp;amp;</code>, thereby causing <code>\r</code> to be expanded to <code>&amp;amp;#xA;</code>.</p> <pre><code> public override void WriteString(string text) { text = text.Replace("\r", "&amp;#xD;"); text = text.Replace("\n", "&amp;#xA;"); text = text.Replace("\t", "&amp;#x9;"); base.WriteString(text); } </code></pre> <p>Finally, to save your <code>XDocument</code> into a destination file or stream, just use the following snippet:</p> <pre><code>using (var textWriter = new StreamWriter(destination)) using (var xmlWriter = new EntitizingXmlWriter(textWriter)) document.Save(xmlWriter); </code></pre> <p>Hope this helps!</p> <p><strong>Edit</strong>: For reference, here is an optimized version of the overridden <code>WriteString</code> method:</p> <pre><code>public override void WriteString(string text) { // The start index of the next substring containing only non-entitized characters. int start = 0; // The index of the current character being checked. for (int curr = 0; curr &lt; text.Length; ++curr) { // Check whether the current character should be entitized. char chr = text[curr]; if (chr == '\r' || chr == '\n' || chr == '\t') { // Write the previous substring of non-entitized characters. if (start &lt; curr) base.WriteString(text.Substring(start, curr - start)); // Write current character, entitized. base.WriteCharEntity(chr); // Next substring of non-entitized characters tentatively starts // immediately beyond current character. start = curr + 1; } } // Write the trailing substring of non-entitized characters. if (start &lt; text.Length) base.WriteString(text.Substring(start, text.Length - start)); } </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload