Creating an XmlNode/XmlElement in C# without an XmlDocument?

I have a simple class that essentially just holds some values. I have overridden the ToString() method to return a nice string representation.

Now, I want to create a ToXml() method, that will return something like this:

<Song>
    <Artist>Bla</Artist>
    <Title>Foo</Title>
</Song>

Of course, I could just use a StringBuilder here, but I would like to return an XmlNode or XmlElement, to be used with XmlDocument.AppendChild.

I do not seem to be able to create an XmlElement other than calling XmlDocument.CreateElement, so I wonder if I have just overlooked anything, or if I really either have to pass in either a XmlDocument or ref XmlElement to work with, or have the function return a String that contains the XML I want?


Solution 1:

I would recommend to use XDoc and XElement of System.Xml.Linq instead of XmlDocument stuff. This would be better and you will be able to make use of the LINQ power in querying and parsing your XML:

Using XElement, your ToXml() method will look like the following:

public XElement ToXml()
{
    XElement element = new XElement("Song",
                        new XElement("Artist", "bla"),
                        new XElement("Title", "Foo"));

    return element;
}

Solution 2:

From W3C Document Object Model (Core) Level 1 specification (bold is mine):

Most of the APIs defined by this specification are interfaces rather than classes. That means that an actual implementation need only expose methods with the defined names and specified operation, not actually implement classes that correspond directly to the interfaces. This allows the DOM APIs to be implemented as a thin veneer on top of legacy applications with their own data structures, or on top of newer applications with different class hierarchies. This also means that ordinary constructors (in the Java or C++ sense) cannot be used to create DOM objects, since the underlying objects to be constructed may have little relationship to the DOM interfaces. The conventional solution to this in object-oriented design is to define factory methods that create instances of objects that implement the various interfaces. In the DOM Level 1, objects implementing some interface "X" are created by a "createX()" method on the Document interface; this is because all DOM objects live in the context of a specific Document.

AFAIK, you can not create any XmlNode (XmlElement, XmlAttribute, XmlCDataSection, etc) except XmlDocument from a constructor.

Moreover, note that you can not use XmlDocument.AppendChild() for nodes that are not created via the factory methods of the same document. In case you have a node from another document, you must use XmlDocument.ImportNode().

Solution 3:

You may want to look at how you can use the built-in features of .NET to serialize and deserialize an object into XML, rather than creating a ToXML() method on every class that is essentially just a Data Transfer Object.

I have used these techniques successfully on a couple of projects but don’t have the implementation details handy right now. I will try to update my answer with my own examples sometime later.

Here's a couple of examples that Google returned:

XML Serialization in .NET by Venkat Subramaniam http://www.agiledeveloper.com/articles/XMLSerialization.pdf

How to Serialize and Deserialize an object into XML http://www.dotnetfunda.com/articles/article98.aspx

Customize your .NET object XML serialization with .NET XML attributes http://blogs.microsoft.co.il/blogs/rotemb/archive/2008/07/27/customize-your-net-object-xml-serialization-with-net-xml-attributes.aspx

Solution 4:

XmlNodes come with an OwnerDocument property.

Perhaps you can just do:

//Node is an XmlNode pulled from an XmlDocument
XmlElement e = node.OwnerDocument.CreateElement("MyNewElement");
e.InnerText = "Some value";
node.AppendChild(e);

Solution 5:

You could return an XmlDocument for the ToXML method in your class, then when you are going to add the Element with the result document just use something like:

XmlDocument returnedDocument = Your_Class.ToXML();

XmlDocument finalDocument = new XmlDocument();
XmlElement createdElement = finalDocument.CreateElement("Desired_Element_Name");
createdElement.InnerXML = docResult.InnerXML;
finalDocument.AppendChild(createdElement);

That way the entire value for "Desired_Element_Name" on your result XmlDocument will be the entire content of the returned Document.

I hope this helps.