kbaccouche
kbaccouche

Reputation: 4605

XDocument write specific XElement in a single line

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");

Upvotes: 2

Views: 604

Answers (1)

dbc
dbc

Reputation: 116876

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.

Upvotes: 2

Related Questions