Mark S. Rasmussen improve.dk
Oct 20
2007

I do a lot of backend programming for Flash frontends. That basically means a lot of ASPX pages that simply return some XML and accept some incoming XML for parameters. Most of the UI logic ends up getting cluttered with manual XML stringbuilding, so I saw this as an obvious opportunity to play around with a fluent interfaces.

Now, here’s an example of a typical boolean yes/no result from a Flash query:

<?xml version="1.0" encoding="utf-8"?>
<root>
	<result type="boolean">true</result>
</root>

I’d usually create this bit of XML using a simple StringBuilder like so:

StringBuilder output = new StringBuilder();
output.Append("<?xml version="1.0" encoding="utf-8"?>");
output.Append("<root>");
output.Append("<result type="boolean">true</result>");
output.Append("</root>");

This has the advantage of being very fast to write, but readability suffers from the escaped quotes, lack of indentation and there’s a whole lot of text when the XML becomes just a bit more advanced.

A “prettier” way is to use the DOM through the XmlDocument like so:

XmlDocument xd = new XmlDocument();
xd.AppendChild(xd.CreateXmlDeclaration("1.0", "utf-8", ""));
 
XmlNode root = xd.CreateElement("root");
xd.AppendChild(root);
 
XmlNode result = xd.CreateElement("result");
result.InnerText = "true";
 
XmlAttribute type = xd.CreateAttribute("type");
type.Value = "boolean";
 
result.Attributes.Append(type);
root.AppendChild(result);

While this does produce exactly the same XML, it takes up twice as many lines of code, excluding the whitespace lines. Without whitespace it is even more unreadable.

Let me introduce you to my quick’n'simple fluent interface that uses XmlDocument internally, XmlOutput:

XmlOutput xo = new XmlOutput()
	.XmlDeclaration()
	.Node("root").Within()
		.Node("result").Attribute("type", "boolean").InnerText("true");

Using XmlOutput we’re down to four lines, the shortest example yet. While linecount is not, and should not be, a measurement of quality, it is preferred. I believe using XmlOutput is, by far, the most readable example.

Basically, using Node() creates a new node within the current node. If no node has been created previously, it will automatically be the root node. Any time a new node is created, it automatically becomes the “current node”. Calling Within() moves the context into the current node, thus any newly created nodes will be created within that node. Attribute() will add an attribute to the current node, likewise will InnerText() set the InnerText of the current node. EndWithin() moves the context to the parent node, it is not mandatory for “closing” the nodes, it is only required when you actually need to move the scope.

Let me present you with a couple of examples. Dynamic data:

XmlOutput xo = new XmlOutput()
	.XmlDeclaration()
	.Node("root").Within()
		.Node("numbers").Within();
 
for (int i = 1; i <= 10; i++)
	xo.Node("number").Attribute("value", i.ToString()).InnerText("This is the number: " + i);
<?xml version="1.0" encoding="utf-8"?>
<root>
	<numbers>
		<number value="1">This is the number: 1</number>
		<number value="2">This is the number: 2</number>
		<number value="3">This is the number: 3</number>
		<number value="4">This is the number: 4</number>
		<number value="5">This is the number: 5</number>
		<number value="6">This is the number: 6</number>
		<number value="7">This is the number: 7</number>
		<number value="8">This is the number: 8</number>
		<number value="9">This is the number: 9</number>
		<number value="10">This is the number: 10</number>
	</numbers>
</root>

And complex structures:

XmlOutput xo = new XmlOutput()
	.XmlDeclaration()
	.Node("root").Within()
		.Node("user").Within()
			.Node("username").InnerText("orca")
			.Node("realname").InnerText("Mark S. Rasmussen")
			.Node("description").InnerText("I'll handle any escaping (like < & > for example) needs automagically.")
			.Node("articles").Within()
				.Node("article").Attribute("id", "25").InnerText("Handling DBNulls")
				.Node("article").Attribute("id", "26").InnerText("Accessing my privates")
				.EndWithin()
			.Node("hobbies").Within()
				.Node("hobby").InnerText("Fishing")
				.Node("hobby").InnerText("Photography")
				.Node("hobby").InnerText("Work");
<?xml version="1.0" encoding="utf-8"?>
<root>
	<user>
		<username>orca</username>
		<realname>Mark S. Rasmussen</realname>
		<description>I'll handle any escaping (like < & > for example) needs automagically.</description>
		<articles>
			<article id="25">Handling DBNulls</article>
			<article id="26">Accessing my privates</article>
		</articles>
		<hobbies>
			<hobby>Fishing</hobby>
			<hobby>Photography</hobby>
			<hobby>Work</hobby>
		</hobbies>
	</user>
</root>

Finally, say hello to XmlOutput:

using System.Xml;
using System.Collections.Generic;
 
public class XmlOutput
{
	// The internal XmlDocument that holds the complete structure.
	XmlDocument xd = new XmlDocument();
 
	// A stack representing the hierarchy of nodes added. nodeStack.Peek() will always be the current node scope.
	Stack<XmlNode> nodeStack = new Stack<XmlNode>();
 
	// Whether the next node should be created in the scope of the current node.
	bool nextNodeWithin;
 
	// The current node. If null, the current node is the XmlDocument itself.
	XmlNode currentNode;
 
	/// <summary>
	/// Returns the string representation of the XmlDocument.
	/// </summary>
	/// <returns>A string representation of the XmlDocument.</returns>
	public string GetOuterXml()
	{
		return xd.OuterXml;
	}
 
	/// <summary>
	/// Returns the XmlDocument
	/// </summary>
	/// <returns></returns>
	public XmlDocument GetXmlDocument()
	{
		return xd;
	}
 
	/// <summary>
	/// Changes the scope to the current node.
	/// </summary>
	/// <returns>this</returns>
	public XmlOutput Within()
	{
		nextNodeWithin = true;
 
		return this;
	}
 
	/// <summary>
	/// Changes the scope to the parent node.
	/// </summary>
	/// <returns>this</returns>
	public XmlOutput EndWithin()
	{
		if (nextNodeWithin)
			nextNodeWithin = false;
		else
			nodeStack.Pop();
 
		return this;
	}
 
	/// <summary>
	/// Adds an XML declaration with the most common values.
	/// </summary>
	/// <returns>this</returns>
	public XmlOutput XmlDeclaration() { return XmlDeclaration("1.0", "utf-8", ""); }
 
	/// <summary>
	/// Adds an XML declaration to the document.
	/// </summary>
	/// <param name="version">The version of the XML document.</param>
	/// <param name="encoding">The encoding of the XML document.</param>
	/// <param name="standalone">Whether the document is standalone or not. Can be yes/no/(null || "").</param>
	/// <returns>this</returns>
	public XmlOutput XmlDeclaration(string version, string encoding, string standalone)
	{
		XmlDeclaration xdec = xd.CreateXmlDeclaration(version, encoding, standalone);
		xd.AppendChild(xdec);
 
		return this;
	}
 
	/// <summary>
	/// Creates a node. If no nodes have been added before, it'll be the root node, otherwise it'll be appended as a child of the current node.
	/// </summary>
	/// <param name="name">The name of the node to create.</param>
	/// <returns>this</returns>
	public XmlOutput Node(string name)
	{
		XmlNode xn = xd.CreateElement(name);
 
		// If nodeStack.Count == 0, no nodes have been added, thus the scope is the XmlDocument itself.
		if (nodeStack.Count == 0)
		{
			xd.AppendChild(xn);
 
			// Automatically change scope to the root DocumentElement.
			nodeStack.Push(xn);
		}
		else
		{
			// If this node should be created within the scope of the current node, change scope to the current node before adding the node to the scope element.
			if (nextNodeWithin)
			{
				nodeStack.Push(currentNode);
 
				nextNodeWithin = false;
			}
 
			nodeStack.Peek().AppendChild(xn);
		}
 
		currentNode = xn;
 
		return this;
	}
 
	/// <summary>
	/// Sets the InnerText of the current node without using CData.
	/// </summary>
	/// <param name="text"></param>
	/// <returns></returns>
	public XmlOutput InnerText(string text)
	{
		return InnerText(text, false);
	}
 
	/// <summary>
	/// Sets the InnerText of the current node.
	/// </summary>
	/// <param name="text">The text to set.</param>
	/// <returns>this</returns>
	public XmlOutput InnerText(string text, bool useCData)
	{
		if (useCData)
			currentNode.AppendChild(xd.CreateCDataSection(text));
		else
			currentNode.AppendChild(xd.CreateTextNode(text));
 
		return this;
	}
 
	/// <summary>
	/// Adds an attribute to the current node.
	/// </summary>
	/// <param name="name">The name of the attribute.</param>
	/// <param name="value">The value of the attribute.</param>
	/// <returns>this</returns>
	public XmlOutput Attribute(string name, string value)
	{
		XmlAttribute xa = xd.CreateAttribute(name);
		xa.Value = value;
 
		currentNode.Attributes.Append(xa);
 
		return this;
	}
}

Enjoy!

Mark S. Rasmussen
I'm the Technical Lead at iPaper where I cuddle with databases, mold code and maintain the overall technical responsibility. I'm an avid speaker at user groups & conferences. I love life, motorcycles, photography and all things technical. Say hi on Twitter, write me an email or look me up on LinkedIn.
Comments
  • Niels Ladegaard Beck October 24, 2007

    Nice! But why not just use LINQ? With the new XML features in .NET 3.5, you can write your last (complex) example like this:

    XDocument xd = new XDocument(
    new XDeclaration("1.0", "UTF-8", "yes"),
    new XElement("root",
    new XElement("user",
    new XElement("username", "orca"),
    new XElement("realname", "Mark S. Rasmussen"),
    new XElement("description", "I’ll handle any escaping (like < & > for example) needs automagically."),
    new XElement("articles",
    new XElement("article", new XAttribute("id", "25"), "Handling DBNulls"),
    new XElement("article", new XAttribute("id", "26"), "Accessing my privates")),
    new XElement("hobbies",
    new XElement("hobby", "Fishing"),
    new XElement("hobby", "Photography"),
    new XElement("hobby", "Work")))));

    It gives you the same XML :-)

    Or, if you prefer VB.NET (who does?!?):

    Dim xd As XDocument = _
    <?xml version="1.0" encoding="utf-8"?>
    <root>
    <user>
    <username>orca</username>
    <realname>Mark S. Rasmussen</realname>
    <description>I’ll handle any escaping (like … for example) needs automagically.</description>
    <articles>
    <article id="25">Handling DBNulls</article>
    <article id="26">Accessing my privates</article>
    </articles>
    <hobbies>
    <hobby>Fishing</hobby>
    <hobby>Photography</hobby>
    <hobby>Work</hobby>
    </hobbies>
    </user>
    </root>

    I’ve newer used VB.NET before, so I couldn’t figure out how to escape the special characters – but I’m sure you know how to do it ;-)

  • Niels Ladegaard Beck October 24, 2007

    When I posted the previous comment, the indentation was correct – it’s just this blog that doesn’t understand spaces ;-)

  • Mark S. Rasmussen October 24, 2007

    While it is true that LINQ simplifies the syntax, it is – as you say yourself – .NET 3.5 only, in a lot of situations you do not have the luxury to be running 3.5. Furthermore I actually much prefer my fluent syntax over the LINQ syntax (I won’t even get into the VB.NET one, yuck), but that is of course of question of personal preference.

    Sorry about the spaces, I’ll look into it ;)

  • Matt Berseth February 22, 2008

    Well done. I wish this would have posted this 5 years ago ;)

  • Chris Pietschmann February 22, 2008

    Very Nice. This is simple, but makes the code so much easier to read. It’s definately a step in the right direction.

  • Andrew February 25, 2008

    Is there a particular reason why you didn’t just use XmlWriter? It isn’t fluent but you could use XmlOutput to wrap it instead of XmlDocument?

    The big advantage there is that XmlWriter does the stack management for you:

    writer.WriteStartElement("foo");
    writer.WriteAttributeString("bar", "baz");
    writer.WriteEndElement();

    It’s also much faster because it streams the Xml directly to the target Stream/TextWriter rather than building the DOM in memory.

  • Mark S. Rasmussen February 25, 2008

    Andrew,

    The reason I used the XmlDocument is that I’m often using the XmlOutput class for creating the XmlDocument instance, and then I’ll continue working on the instance in the calling code.

    Granted, XmlWriter is faster for just spitting out a lot of XML, but it doesn’t give the opportunity of retrieving the XmlDocument afterwards. One could create an XmlOutputWriter class that mimics XmlOutput.

  • Bill Booth February 25, 2008

    <snip>
    Furthermore I actually much prefer my fluent syntax over the LINQ syntax (I won’t even get into the VB.NET one, yuck), but that is of course of question of personal preference.
    </snip>

    I don’t understand this. The only VB I noticed was the XDocument declaration. That is yuck? It looks more like the xml desired than anything on the page.

  • Mark S. Rasmussen February 25, 2008

    Bill,

    There’s no VB in my code samples at all. I’m talking about the VB specific language feature where you can simply embed XML directly into the source code. It sure makes it easy to write out XML, but I personally do not like mixing in XML markup with the source code.

  • Cihan Ucar February 25, 2008

    Awesome piece of code! It is worth to refactor existing code which produces xml.

  • Justin Etheredge February 25, 2008

    Very nice use of a fluent interface! So simple, yet so powerful. </jealousness>

  • Jon von Gillern February 25, 2008

    I’m not sure if anything could be easier to work with than the XmlSerializer. If you’re going to be spitting out or sucking in Xml, you owe it to yourself to look into it.

    http://www.vonsharp.net

  • Mark S. Rasmussen February 25, 2008

    Jon,

    I don’t want to have to create classes for all the various forms of XML that I might be spitting out. Also, as people mentioned, XmlDocument might not be the best performing – but compared to reflection based serialization, it’s blazingly fast.

    Now, if I can serialize anonymous classes that I create, that might be a possibility, but it’d still not be nowhere near as concise as the XmlOutput class is.

  • Bacardi February 27, 2008

    Reminds me of jQuery. Very nice. Great work! It’s now a part of the collection.

  • Steve Trefethen February 27, 2008

    Thanks for this post, I’m doing a lot of XML right now and this may prove to be a real time saver not to mention making for more readable code.

  • Ken Egozi February 27, 2008

    Very nice and all, nicer API than the default System.XML, but since XML is essentially text, Id use templating solutions for that. See http://www.kenegozi.com/...

  • Pingback

  • Shawn Oster February 29, 2008

    Reminds me a lot of CMarkup over at http://www.firstobject.com/. I actually ported CMarkup way back when it was free from codeproject into Delphi and that’s my XML builder of choice these days. I just wrapped your cool fluent interface around CMarkup (in Delphi no less) and I have to say, it’s a pleasure to use!

    As an aside, who in the world was *ever* using just a StringBuilder to create their markup? Seriously people, you’ve had XmlWriter since 1.0, how did anyone ever think a SB was a good idea?

  • James Curran March 3, 2008

    I like the API, except for two minor details.

    .Node("root").Within().Node("result")

    seems to me to say that <root> is within <result> instead of the other way around. I might suggest "Wraps()".

    Next is XmlDeclaration(). It accepts three parameters, all string. I know this matches the XmlDocument method, but it’s still dumb. Properly the parameters should be a float, an enum, and a boolean. I might even go for have a overload that accepts a System.Text.Encoding object as it’s second parameter.

  • Kris Vandermotten March 28, 2008

    <Jon>I’m not sure if anything could be easier to work with than the XmlSerializer. If you’re going to be spitting out or sucking in Xml, you owe it to yourself to look into it.</Jon>

    <Mark>I don’t want to have to create classes for all the various forms of XML that I might be spitting out. Also, as people mentioned, XmlDocument might not be the best performing – but compared to reflection based serialization, it’s blazingly fast.</Mark>

    Oh? And of course you know, bacause you’ve benchmarked it?

    Well written Xml serializable classes are allways a lot faster to manipulate than the DOM, also when serializing and deserializing. I know they are, because I did benchmark (and decompiled the generated code).

    Sure, on the first run over a small document, the serializer is somewhat slow, because it needs to emit the code that uses the XmlWriter (or XmlReader) to (de)serialize. That’s reflection indeed, with Reflection.Emit. For a website, that shouldn’t matter at all, but if you don’t want to take the hit, use sgen.exe.

    Jon is right. Look into it.

    And BTW, you might want to look at .ashx files as well.

  • Mark S. Rasmussen March 29, 2008

    Kris,

    Actually I didn’t "know" per se, I had a hunch. Your comment made me want to test it out:
    improve.dk/…
    XmlOutput is only used to generate Xml, not to read and manipulate it, so that is really not an argument against the XmlOutput class.

    I’d very much like to see your own performance results & code to see how you got it to be so fast compared to XmlOutput/XmlDocument.

    Oh, and yes, I have "heard" about ASHX files :)

  • Brandon Morales August 1, 2008

    I took some of the suggestions made in the comments, and followed up on your suggestion that your class could be redone with similar functionality using XmlWriter. It still allows for output as XmlDocument though.

    Thanks for the wonderful inspiration, your class really made a difference in my Flash development.

  • Denis Kudriashov September 15, 2008

    What you think about this syntax:

    Xml xml = new Xml()
    xml.With("root", ()=>
    {
    xml.With("user", ()=>
    {
    xml
    .With("username", "orca")
    .With("realname","Mark S. Rasmussen")
    .With("description""I’ll handle any escaping (like < & > for example) needs automagically.")
    .With("articles",()=>
    {
    xml
    .With("article",()=>
    {
    xml
    .WithAttr("id", "25")
    .With("Handling DBNulls")
    })
    .With("article",()=>
    {
    xml
    .WithAttr("id", "26")
    .With("Accessing my privates")
    });
    })
    .With("hobbies",()=>
    {
    xml
    .With("hobby","Fishing")
    .With("hobby", "Photography")
    .With("hobby", "Work");
    });
    });
    });

    Moreover, we can replace With() keyword with index []:
    xml["user", ()=>
    {
    xml
    ["name", "John"]
    ["address", "Moscow"];
    }

    In smalltalk language we have more elegant interface:

    xml user:
    [
    xml
    name: 'John';
    address: 'Moscow';
    ]

  • Steve Trefethen November 19, 2008

    Wouldn’t it make sense to assign currentNode = nodeStack.Pop() in EndsWithin()?

  • Mark S. Rasmussen November 25, 2008

    Hi Steve,

    I’m not really sure. As it is now, Within & EndWithin only control stack flow and nothing else. The currentNode variable is what defines what will be modified when we add attributes, inner text. If for instance we went:

    Node(A).Within().Node(X).EndsWithin().Attribte("test", "value");

    As it is now, the attribute will be put onto the very lastly added node – X. If we change the currentNode value in EndsWithin, the attribute will be added to node A – a node that may be very long away from our current code point, which will kinda mess up the fluentness of the interface.

    Also, usually I’ll be adding attributes before adding child nodes to a node, thus rendering the issue trivial.

    I’m open to arguments :)

    / Mark S. Rasmussen

  • Steve Trefethen November 25, 2008

    Hi Mark,
    Thanks for the follow-up. While I can appreciate your example I’d fully expect the Attribute to be added to "A" even though it’s "far away". Personally, I’d find it strange to use EndsWithin() then followed by a call to Attributes thinking it will at attributes to a node I just closed out.

    I had a case where I wanted to build an XML document inside of a for loop and at the end add attributes to the root node of the document with summary counts. In that case, I made the change I suggested and could then simply add EndWithin() calls to "pop" the currentnode back up to the top of the stack and then call .Attribute().

    Anyway, thanks again for the post.

  • Jason Sun December 15, 2008

    I am using your class and I have a shorthand question, currently I have an output like this:
    <Feature>
    <FCat>Packages</FCat>
    <FType>OF</FType>
    <FName>
    </FName>
    <FPrice>0.0</FPrice>
    <FDetDesc>
    </FDetDesc>
    </Feature>

    Is there possible that I can change:
    <FName>
    </FName>
    and
    <FDetDesc>
    </FDetDesc>
    to:
    <FName and
    <FDetDesc
    ?

    Thanks

  • Jason Sun December 15, 2008

    I guess I didn’t explain clearly…

    this is the simplfied soruce code:

    foreach(AutoFeature featureItem in FeatureList) {
    xo.Node("Feature").Within();
    xo.Node("FCat").InnerText(featureItem.FCat);
    xo.Node("FType").InnerText(featureItem.FType);
    xo.Node("FName").InnerText(featureItem.FName);
    xo.Node("FPrice").InnerText(featureItem.FPrice);
    xo.Node("FDetDesc").InnerText(featureItem.FDetDesc);
    xo.EndWithin();
    }

    and as long as any field = null or "" in the InnerText(field), the tag will become

    <TAG>
    </TAG>

    instead of <TAG

    so my quesion is how to change to get shorthand tag?

  • Noman May 27, 2011

    I know they are, because I did benchmark (and decompiled the generated code).

    Okmulgee Lawyer

  • khach san vietnam July 28, 2012

    Fantastic post, it’s being mentioned in a lot recently topics but with that said yours is by far the most informative post I have read. I am looking for sites to leave a guest post on if your interested please contact via the email I have supplied in this comment. Thanks

  • Clarence H. July 30, 2012

    A blog can never be questioned because you are free anything you want but I highly and positively questioned the blogger for posting this kind of blog.

  • Asynth September 13, 2012

    I’m implementing it in Unity which supports a subset of .NET without LINQ.
    It’s simple, clear and efficient.
    Thank you !

  • online tv October 29, 2012

    Your blog provided us with valuable information to work with. Each & every tips of your post are awesome. Thanks a lot for sharing. Keep blogging. Thanks for the valuable post.. very nice.. traffic reviews

  • Justin November 2, 2012

    I’m not sure why this is better than doing something like the code below. I actually can’t get mine to work properly but you’ll get the idea. Seems much more concise than your approach.

    string xmlText=@"
    - multiline xml
    - indentation is ok with c# text editor
    - double quote replaced by single quote
    ";
    xmlText = xmlText.Replace("’",""");
    var xmlDocument = new XmlDocument();
    xmlDocument.LoadXml(xmlText);

  • Mark S. Rasmussen November 4, 2012

    If it works for you, by all means, keep it simple.

    I prefer using an XmlOutput-like method since I avoid all of the string escaping, @’s and improperly indented output when I pass the StringBuilder/string between different functions & contexts. So many things that can go wrong unless you really know what you’re doing. I also don’t want to manually take care of details like CDATA, attribute value escaping and so forth.

  • cheesecake November 15, 2012

    Hi! This is my first visit to your blog! We are a team of volunteers and new initiatives in the same niche. Blog gave us useful information to work. You have done an amazing job!

  • seo dubai November 24, 2012

    As soon as a careful browse I thought it was really enlightening. I take pleasure in you taking the time and effort to put this blog post together. I once again discover me personally spending way to much time both reading and leaving comments.

  • friv November 28, 2012

    This is a nice blog i must say, usually i don’t post comments on others’ blogs

Leave a Comment