Search code examples
c#xmlxmlwriter

c# XlmWriter auto indenting string in written text


I need to write an xml in C# and I want to make the text formatted. These are my settings:

XmlWriterSettings settings = new XmlWriterSettings
{
    Indent = true,
    IndentChars = ("\t"),
    NewLineChars = "\r\n",
    NewLineHandling = NewLineHandling.Replace
};

XmlWriter xmlWriter = XmlWriter.Create(path + "//" + nome, settings);

After the header and the first nodes I should insert this string:

string st = "\r\nLINE 1\r\nLINE 2\r\nLINE 3\r\n";

And I would like to this to be formatted as follows:

    <Text>
          LINE 1
          LINE 2
          LINE 3
    </Text>

I wrote this code:

xmlWriter.WriteString(st);

Solution

  • To achieve such a result you should implement a custom XmlWriter:

    public class IndentTextXmlWriter : XmlTextWriter
    {
        private int indentLevel;
        private bool isInsideAttribute;
    
        public IndentTextXmlWriter(TextWriter textWriter): base(textWriter)
        {
        }
    
        public bool IndentText { get; set; }
    
        public override void WriteStartAttribute(string prefix, string localName, string ns)
        {
            isInsideAttribute = true;
            base.WriteStartAttribute(prefix, localName, ns);
        }
    
        public override void WriteEndAttribute()
        {
            isInsideAttribute = false;
            base.WriteEndAttribute();
        }
    
        public override void WriteStartElement(string prefix, string localName, string ns)
        {
            indentLevel++;
            base.WriteStartElement(prefix, localName, ns);
        }
    
        public override void WriteEndElement()
        {
            indentLevel--;
            base.WriteEndElement();
        }
    
        public override void WriteString(string text)
        {
            if (String.IsNullOrEmpty(text) || isInsideAttribute || Formatting != Formatting.Indented || !IndentText || XmlSpace == XmlSpace.Preserve)
            {
                base.WriteString(text);
                return;
            }
    
            string[] lines = text.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
            string indent = new string(IndentChar, indentLevel * Indentation);
            foreach (string line in lines)
            {
                WriteRaw(Environment.NewLine);
                WriteRaw(indent);
                WriteRaw(line.Trim());
            }
    
            WriteRaw(Environment.NewLine);
            WriteRaw(new string(IndentChar, (indentLevel - 1) * Indentation));
        }
    }
    

    You can use it like this:

    [TestMethod]
    public void WriteIndentedText()
    {
        var result = new StringBuilder();
        using (var writer = new IndentTextXmlWriter(new StringWriter(result)){Formatting = Formatting.Indented, IndentText = true})
        {
            string text = @" Line 1
    Line 2
        Line 3  ";
            // some root
            writer.WriteStartDocument();
            writer.WriteStartElement("root");
            writer.WriteStartElement("child");
    
            // test auto-indenting
            writer.WriteStartElement("elementIndented");
            writer.WriteString(text);
            writer.WriteEndElement();
    
            // test space preserving
            writer.WriteStartElement("elementPreserved");
            writer.WriteAttributeString("xml", "space", null, "preserve");
            writer.WriteString(text);
    
            writer.WriteEndDocument();
        }
    
        Debug.WriteLine(result.ToString());
    }
    

    And the output:

    <?xml version="1.0" encoding="utf-16"?>
    <root>
      <child>
        <elementIndented>
          Line 1
          Line 2
          Line 3
        </elementIndented>
        <elementPreserved xml:space="preserve"> Line 1
    Line 2
        Line 3  </elementPreserved>
      </child>
    </root>