2

I am writing an XML file using XDocument. I want to make adjustments to the file layout. Let me explain, here is an extract of the generated file:

<ROOTELEMENT>
  <CHILDELEMENT>
    <INFO1>Test a 1</INFO1>
    <INFO2>Test a 2</INFO2>
  </CHILDELEMENT>
  <CHILDELEMENT>
    <INFO1>Test b 1</INFO1>
    <INFO2>Test b 2</INFO2>
  </CHILDELEMENT>
<ROOTELEMENT>

I want my file to look like this instead :

<ROOTELEMENT>
  <CHILDELEMENT><INFO1>Test a 1</INFO1><INFO2>Test a 2</INFO2></CHILDELEMENT>
  <CHILDELEMENT><INFO1>Test b 1</INFO1><INFO2>Test b 2</INFO2></CHILDELEMENT>
</ROOTELEMENT>

Here is my code:

var myDoc = new XDocument(new XElement("ROOTELEMENT",
                                    new XElement("CHILDELEMENT",
                                        new XElement("INFO1", "Test a 1"),
                                        new XElement("INFO2", "Test a 2")),
                                    new XElement("CHILDELEMENT",
                                        new XElement("INFO1", "Test b 1"),
                                        new XElement("INFO2", "Test b 2"))));

myDoc.Save("Test.xml");
dbc
  • 104,963
  • 20
  • 228
  • 340
kbaccouche
  • 4,575
  • 11
  • 43
  • 65
  • 1
    Is it really matter? Looks like a formatting issue mostly – Pavel Anikhouski Jul 31 '19 at 09:48
  • Yes it's a formatting issue. I am replacing another application written in another language and I want the output to be exactly as it was before I come along with my new application. – kbaccouche Jul 31 '19 at 09:50
  • 1
    I mean, the indentation of the XML doesn't really matter lmao. Any halfway decent XML parser will ignore it, I'm not even sure if there's something that does not – Abana Clara Jul 31 '19 at 09:56
  • To my client, it does. I'm guessing to save memory and still keep the file readable. – kbaccouche Jul 31 '19 at 12:08
  • Possible duplicate of [How do you create an indented XML string from an XDocument in c#?](https://stackoverflow.com/questions/2374654/how-do-you-create-an-indented-xml-string-from-an-xdocument-in-c) – OfirD Aug 14 '19 at 08:43

1 Answers1

2

Formatting of XML output is controlled by XmlWriter not XElement or XDocument, so if you need precise control of formatting you will need to create your own writer by subclassing one of the implementations of XmlWriter, specifically XmlTextWriter whose Formatting property is mutable and can be changed during writing.

For instance, here is a version that disables indentation when the element depth exceeds 1:

public class CustomFormattingXmlTextWriter : XmlTextWriter
{
    readonly Stack<Formatting> stack = new Stack<Formatting>();

    public CustomFormattingXmlTextWriter(TextWriter writer, int indentDepth) : base(writer) 
    { 
        this.Formatting = Formatting.Indented; 
        this.IndentDepth = indentDepth;
    }
    
    int IndentDepth { get; }

    void OnElementStarted(string localName, string ns)
    {
        stack.Push(Formatting);
        // You could e.g. modify the logic here to check to see whether localName == CHILDELEMENT
        // if (localName == "CHILDELEMENT")
        if (stack.Count == IndentDepth+1)
            Formatting = Formatting.None;
    }

    void OnElementEnded()
    {
        var old = stack.Pop();
        if (old != Formatting)
            Formatting = old;
    }

    public override void WriteStartElement(string prefix, string localName, string ns)
    {
        base.WriteStartElement(prefix, localName, ns);
        OnElementStarted(localName, ns);
    }

    public override void WriteEndElement()
    {
        base.WriteEndElement();
        OnElementEnded();
    }
    
    public override void WriteFullEndElement()
    {
        base.WriteEndElement();
        OnElementEnded();
    }
}

public static partial class XNodeExtensions
{
    public static void SaveWithCustomFormatting(this XDocument doc, string filename, int indentDepth)
    {
        using (var textWriter = new StreamWriter(filename))
        using (var writer = new CustomFormattingXmlTextWriter(textWriter, indentDepth))
        {
            doc.Save(writer);
        }
    }
}

Using it, you can do:

myDoc.SaveWithCustomFormatting(fileName, 1);

which outputs, as required:

<ROOTELEMENT>
  <CHILDELEMENT><INFO1>Test a 1</INFO1><INFO2>Test a 2</INFO2></CHILDELEMENT>
  <CHILDELEMENT><INFO1>Test b 1</INFO1><INFO2>Test b 2</INFO2></CHILDELEMENT>
</ROOTELEMENT>

Notes:

  • You can modify the logic in CustomFormattingXmlTextWriter.OnElementStarted() to disable the formatting using any criteria you desire such as checking to see whether the incoming localName is CHILDELEMENT.

  • XmlTextWriter is deprecated in favor of XmlWriter -- but the latter does not have a mutable Formatting property. If you must needs use XmlWriter you might look at Is there a way to serialize multiple XElements onto the same line?.

Demo fiddle #1 here.

dbc
  • 104,963
  • 20
  • 228
  • 340