views:

180

answers:

3

How is it possible to force extra spacing between some nodes using Linq to Xml? I am looking to output the following:

<root>

    <group>
        <leaf />
    </group>

    <group>
        <leaf />
    </group>

</root>

By adding Empty XText, it only destroys the formatting.

var root =
    new XElement("root",
        new XText(""),
        new XElement("group",
            new XElement("leaf")),
        new XText(""),
        new XElement("group",
            new XElement("leaf")),
        new XText(""));

Console.WriteLine(root.ToString());

resulting in

<root><group><child /></group><group><child /></group></root>
A: 

Pass an XmlTextWriter (with Formatting set to Formatting.Indented) to root.WriteTo().

For example:

using(var writer = new XmlTextWriter(Console.Out));
{
    writer.Formatting = Formatting.Indented;
    root.WriteTo(writer);
}
Randolpho
`XmlTextWriter` is deprecated.
Pavel Minaev
It most certainly is not
Randolpho
Official documentation (http://msdn.microsoft.com/en-us/library/system.xml.xmltextwriter.aspx) disagrees: "In the .NET Framework version 2.0 release, the recommended practice is to create XmlWriter instances using the XmlWriter.Create method and the XmlWriterSettings class. This allows you to take full advantage of all the new features introduced in this release. For more information, see Creating XML Writers."
Pavel Minaev
That does not equate to deprecation. `XmlWriter.Create()` is essentially a factory method, allowing abstraction to `XmlWriter` rather than instantiating a concrete `XmlTextWriter`. But that is merely best practice, not deprecation. Now, granted, XmlWriter.Create() does offer certain benefits, such as better compliance to XML standards, than `XmlTextWriter` does. But again, that is not deprecation. Both your way and my way are correct enough for the question at hand.
Randolpho
`XmlWriter.Create` is indeed a factory method, but it doesn't create instances of `XmlTextWriter`. It provides access a new set of writer classes, which aren't otherwise accessible (they're all `internal`), and which do have subtle differences with the old 1.x classes such as `XmlTextWriter`. In particular, `XmlTextWriter` performs less validation: it doesn't check for duplicate attributes and invalid characters in element/attribute names, doesn't properly escape Unicode characters that cannot be represented in the target encoding, etc. See http://www.tkachenko.com/blog/archives/000584.html
Pavel Minaev
+1  A: 
using (var writer = XmlWriter.Create(
    Console.Out,
    new XmlWriterSettings { Indent = true })
{
    root.WriteTo(writer);
}
Pavel Minaev
Doesnt work for me. The extra spacing does not show up
Marc-André Bertrand
It seems that I (and not I alone) misunderstood your question - you don't just want indentation, you also want those blank lines between elements. If you want that, you'll need to insert your own `XText` nodes - not empty ones, but with correct amount of `\n` and spaces or tabs to get the exact indentation that you need.
Pavel Minaev
Yea sorry if my question is misleading. The problem with XText is that if you mix it with XElements, it breaks the formatting. Having to explicitely add all the tabs myself is kinda ugly. (ie new XText("\n\t\t"))
Marc-André Bertrand
It doesn't break the formatting, really. It just disables auto-indenting, because, well, auto-indenting would also be text node; and by inserting your own text there, you're saying that you want this exact text, and not something else (like e.g. a bunch of extra spaces); so `XmlWriter` respects that.
Pavel Minaev
A: 

This is a solution but it´s not beautiful...

Change XText to XComment and do something like this...

 
    var root =
        new XElement("root",
            new XComment(""),
            new XElement("group",
                new XElement("leaf")),
            new XComment(""),
            new XElement("group",
                new XElement("leaf")),
            new XComment(""));

    Console.WriteLine(XElementToText(root));


    private string XElementToText(XElement element)
    {
        var sb = new StringBuilder();
        using (var writer = XmlWriter.Create(sb, 
            new XmlWriterSettings {Indent = true}))
        {
            element.WriteTo(writer);
        }
        return sb.ToString().Replace("<!---->", string.Empty);
    }

Edit: Fogott to escape lesser than ...

Jens Granlund
Haha nick hack! Didn't think about this one!Not the best solution but definitively does the job
Marc-André Bertrand