16

For some reason the following code produces XML that does not contain a declaration:

        XDocument xDocument = new XDocument(new XDeclaration("1.0", "utf-8", "yes"),
            new XElement("project",
                new XAttribute("number", project.ProjectNumber),
                new XElement("store",
                    new XAttribute("number", project.StoreNumber)
                ),

                // User Element
                new XElement("user", 
                    new XAttribute("owner-id", project.OwnerID ?? 0),
                    new XElement("email", new XCData(project.OwnerEmail ?? "")),
                    new XElement("project-name", new XCData(project.ProjectName ?? ""))
                ),

                // Nested Project Element
                new XElement("nested-project", 
                    new XAttribute("version", new Version(1, 0)),
                    new XElement("project", 
                        new XAttribute("version", new Version(1, 0)),
                        xProjectItems = new XElement("project-items")
                    ),
                    new XElement("price-per-part", project.PricePerPart),
                    new XElement("sheet-quantity", project.SheetQuantity),
                    new XElement("edge-length", project.EdgeLength),
                    new XElement("price", project.Price),
                    new XElement("status", project.Status),
                    xMaterialItems = new XElement("alternative-material-items"),
                    xProductItems = new XElement("project-product-items")
                )
            )
        );

        String strXML = xDocument.ToString();

It has produced a declaration before. Am I missing something obvious?

Thanks.

Ahmad Mageed
  • 94,561
  • 19
  • 163
  • 174
Jordan
  • 9,642
  • 10
  • 71
  • 141

3 Answers3

26

The XDeclaration will be available when you use one of the XDocument.Save methods. For example:

var doc = new XDocument (
            new XDeclaration ("1.0", "utf-8", "yes"),
            new XElement ("test", "data")
        );

string path = Path.Combine(Path.GetTempPath(), "temp.xml");
doc.Save(path);
Console.WriteLine(File.ReadAllText(path));

Alternately you could use this approach:

var sw = new StringWriter();
doc.Save(sw);
string result = sw.GetStringBuilder().ToString();
Console.WriteLine(result);

EDIT: note that some approaches will convert the utf-8 designation to utf-16. If you want to force it to be utf-8 you would have to use this different approach:

using (var mem = new MemoryStream())  
using (var writer = new XmlTextWriter(mem, System.Text.Encoding.UTF8))
{
    writer.Formatting = Formatting.Indented;
    doc.WriteTo(writer);
    writer.Flush();
    mem.Flush();
    mem.Seek(0, SeekOrigin.Begin);
    using (var reader = new StreamReader(mem))
    {
        var xml = reader.ReadToEnd();
        Console.WriteLine(xml);
    }
}
Ahmad Mageed
  • 94,561
  • 19
  • 163
  • 174
  • 1
    That's lame. Not your solution (which is pretty cool-- I didn't realize you could do that with a `StringWriter`). Its lame that Microsoft would do that. I'm not saving this XML to a file, I'm passing it into a stream. What's the difference between an 'XDocument' and an 'XElement'. I mean its good that they are alike, but there should be some distinction. And if someone doesn't want the declaration in the string then you could just do: `xMyDocument.Root.ToString();` – Jordan Jun 07 '11 at 19:06
  • @Jordan I suppose the rationale is that an XDocument represents an entire document, and an XElement is a fragment of XML. Despite that, they're very similar. – Ahmad Mageed Jun 07 '11 at 19:20
  • Oh, I know how they are different in general, but in LINQ to XML, an `XDocument` seems to be little more than an `XElement` that cannot be contained. – Jordan Jun 07 '11 at 20:55
3

The documentation does not state explicitly that xDocument.ToString() will output the XML declaration, it only says: "Returns the indented XML for this node.".

In my testing, I found that the following methods will output the declaration:

Use ToString on the Declaration property:

string strXML = string.Concat(xDocument.Declaration.ToString(), "\r\n",
                              xDocument.ToString());

or use the Save method:

string strXml;
using(var ms = new MemoryStream())
{
    xDocument.Save(ms);
    ms.Position = 0;
    using(var sr = new StreamReader(ms))
    {
        strXml = sr.ReadToEnd();
    }
}
rsbarro
  • 27,021
  • 9
  • 71
  • 75
  • Note: I think @Ahmad Mageed's approach with `StringWriter` is the way to go. Same idea but shorter than my `Save` method. – rsbarro Jun 07 '11 at 18:55
  • +1 for the effort. I suppose there is a case for the longer approach, as shown in my edit, when needing to force the encoding. – Ahmad Mageed Jun 07 '11 at 19:17
2

My answer (inspired by the answer by @rsbarro):

string xml = xDocument.Declaration.ToString() +
    xDocument.ToString();

- or -

string xml = xDocument.Declaration.ToString() +
    xDocument.ToString(SaveOptions.DisableFormatting);

As to why I believe use of the + operator is appropriate in this situation, see this answer to the question What's the best string concatenation method using C#?.

Community
  • 1
  • 1
DavidRR
  • 18,291
  • 25
  • 109
  • 191