Posted on June 4, 2008 16:51 by swilliams

XML is kind of a neat thing. It is generally human readable and well supported by everything under the sun. But, like everything else in the programming scope, it is imperfect. The problem I have with it is that it seems like everyone is trying to shoehorn technologies into it.

Let's back up a second. XML is a Declarative Programming Language. This means that it describes a process rather than performing one. The most obvious example of this is HTML*. At a basic level, a web page is just a document, and HTML describes its structure well enough. In other words, all it does is state "Here is a heading. Here is a paragraph..." However, when a web page needs to be manipulated or have of some kind of logical flow, something else must be used. I don't know if this is an intended behavior, but it ends up working very well.

[* Technically speaking, HTML came first, and then XML was created as a superset of it.]

Most "classical" programming languages are considered Procedural (or in some circles, Imperative). Rather than describe something, they state the logical flow of execution. These languages have all the control expressions that people are familiar with: ifs, loops, functions, etc.

Each technology performs quite well in its own domain. But the road to pain and suffering begins when things get mixed up, and Procedural concepts are introduced into a Declarative syntax and vice versa.

The biggest example of this woe is an XML based rules engine that I stumbled across not too long ago. Here is how it handles a conditional statement:

<Logic>
  <If>
    <And>
        <Equals leftId="CLIENT_RATING" rightId="PREMIUM_RATING" />
        <Equals leftId="PRODUCT_TYPE" rightId="REGULAR_TYPE" />
    </And>
    <Do>
        <Integer id="DISCOUNT_PERCENT" value="5" />
    </Do>
  </If>
</Logic> 

Just looking at that makes my skin itchy. Just for contrast, here's the same thing in JavaScript:

if (ClientRating == PremiumRating && ProductType == RegularType) {
  DiscountPercent = 5;
}

A common argument for using XML is that it is easier for a lay-person to read, meaning that a programmer is not needed for maintenance. This may be true for smallish files (say 20 lines or so), but what happens when a non-programmer opens up a thousand line build script to make a substantial change? Once a file gets to be sufficiently large, the maintainer needs to have significant knowledge of the domain to make reliable changes, no matter how "easy" it is to read.

Though the bigger problem with this is that the XML here doesn't describe anything, it actually runs the process!

Build scripts are particular offenders here, MSBuild and NAnt being the bigger fish in the sea. In general I like both of these technologies; they provide incredibly useful services to programmers, but editing and maintaining them is arduous. On the surface, a Declarative language would fit in well here. After all, you are just describing a build. But when you get down and dirty, it becomes apparent that these are really procedures being run.

In both pieces of software, there is the concept of "Tasks," These tasks can clean a directory, compile code, publish it, and more. There isn't actually much of a description going on here, the XML is stating exactly what needs to be done. The individual components of a task are really just the lines of a function:

<csc target="exe" output="HelloWorld.exe" debug="false">
  <sources>
    <include name="**/*.cs" />
  </sources>
  <references>
    <include name="System.dll" />
  </references>
</csc>

Could map directly to this JavaScript:

function compile(target, output, debug) {
  var compiler = new Compiler(target, output, debug);
  compiler.sources.push('*.cs');
  compiler.references.push('System.dll');
  compiler.compile();
}

I've been using JavaScript in these examples for a reason: it lends itself well to the simple procedural programming that is described here. It's fairly lightweight, has a straightforward syntax (especially when certain conventions are forced on it), and most programmers already are familiar with it, due to the ubiquity of it on the Internet. You could have a whole scaffolding of a build script that looks like this:

// Build variables
var codeDir = 'c:\code';
var outDir = 'c:\output';

// Entry Point
function init() {
  clean();
  getFromScm();
  compile();
  test();
  deploy();  
}

// Error Handling
function onError(msg) {
}

// Tasks
function clean() {
}

function getFromScm() {
}

function compile() {
}

function test() {
}

function deploy() {
}

If JavaScript isn't your thing, there is a fascinating build utility out there called Rake. It is billed as a replacement for the old Unix and C Make utility. It does something similar to my JavaScript example, and has the power of Ruby behind it.

For programmers, and even laymen, this is readable and maybe even intuitive. Additionally, you have hosts of technologies that go with a popular procedural languages. Can you create a full battery of unit tests for your build script? Quickly? Can you easily attach them to a debugger? Perform static analysis? All of those things would be useful in a build script, but are difficult to do with XML.

Well then, what exactly is XML good for? In my experience I have found that XML is the "right" solution when you do not have to do any extra processing to handle it. Yes, most modern libraries have XmlReaders and such, but it is still a dreary task to using them to parse a large document. Perhaps the best example I can give is .NET's XmlSerialization modules. With a few lines of code, you can turn an object into an XML node, and then back again. No mucking about with Readers, XPath, or anything. And generally, the resulting XML is lightweight enough that it isn't a huge chore to modify it if needed.

The rule of thumb here is that XML is good when you do not need to put forth a huge effort to use it. Remember, things like this are supposed to be increasing our productivity, not lowering it.

I hope that I didn't come down too hard on XML here. Again, I think it is an incredibly useful tool, and there have been plenty good technologies based on it, I just think that you need to be careful not to use it (or any other "hot" commodity) in the domains that it is simply not suited for.



Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted on March 23, 2008 10:53 by mcollins

Yesterday, in my flurry of posting activity all day long, we covered the Blogger API, the MetaWeblog API, and in a screencast I demonstrated how Microsoft Office Word 2007 would use the MetaWeblog API to communicate with the website and publish text and images to the website using operations from each of those APIs.

Today, we turn our attention to using Windows Live Writer and geting Windows Live Writer to work with our website.  While we could connect Windows Live Writer up to our website directly, just like we did with Word, there are some features supported by Windows Live Writer that make the content publishing experience much easier when the website supports it.

The first feature that we're going to implement to add support for Windows Live Writer is a protocol called Really Simple Discovery (RSD).  RSD is basically an XML document that is placed on your website (or in our case, will be generated by our website using an HTTP handler in ASP.NET).  The XML elements in the RSD document tell a client program such as Windows Live Writer where to locate the MetaWeblog publishing service.  The link to the RSD document is contained in the header of the home page for the blog or website that we're publishing to from Windows Live Writer.

At runtime, what will happen is that we'll start up Windows Live Writer and register a new blog or website with it.  Windows Live Writer will ask us for the URL to the home page for the blog or website, which we'll type in.  Windows Live Writer will download the contents of the home page and will look in the <head> section for an HTML element that will look similar to this:

   1: <html>
   2:     <head>
   3:         ...
   4:         <link rel="EditURI" type="application/rsd+xml"
   5:             title="RSD" href="http://localhost:9500/rsd.ashx"/>
   6:         ...
   7:     </head>
   8:     <body>
   9:         ...
  10:     </body>
  11: </html>

 

The <link> element in the HTML uses the rel attribute to identify the link for RSD.  Once a program such as Windows Live Writer finds this line, Windows Live Writer will be able to use the URL in the href attribute to locate the RSD document (or in our case, our HTTP handler).

While I could have created an XML file for RSD, I chose to go with creating an HTTP handler that will build the RSD document at runtime.  The reason why I chose to do this is that the URLs specified in the RSD document need to be absolute URLs for Windows Live Writer to locate the APIs correctly.  In my HTTP handler, I use the current request information to build the URL for the MetaWeblog API implementation.  Here's the code for the RSD HTTP handler:

   1: public class ReallySimpleDiscoveryHandler : IHttpHandler {
   2:     /// <summary>
   3:     /// Returns true to indicate that the HTTP handler can be reused for
   4:     /// multiple requests.
   5:     /// </summary>
   6:     public bool IsReusable {
   7:         get { return true; }
   8:     }
   9:  
  10:     /// <summary>
  11:     /// Handles the request and serves the RSD document to the client.
  12:     /// </summary>
  13:     /// <param name="context">
  14:     /// The <see cref="HttpContext"/> used to handle the current
  15:     /// request.
  16:     /// </param>
  17:     public void ProcessRequest(HttpContext context) {
  18:         string rootUrl = String.Format("{0}://{1}:{2}/",
  19:             context.Request.Url.Scheme, context.Request.Url.Host,
  20:             context.Request.Url.Port);
  21:  
  22:         XNamespace ns = "http://archipelago.phrasewise.com/rsd";
  23:         XDocument rsdDocument = new XDocument(new XElement(ns + "rsd",
  24:             new XAttribute("version", "1.0"),
  25:             new XElement(ns + "service", "Content Publishing 101"),
  26:             new XElement(ns + "engineLine", 
  27:                 "http://www.sogeti-phoenix.com"),
  28:             new XElement(ns + "homePageLink", rootUrl),
  29:             new XElement(ns + "apis",
  30:                 new XElement(ns + "api",
  31:                     new XAttribute("name", "MetaWeblog"),
  32:                     new XAttribute("preferred", "true"),
  33:                     new XAttribute("apiLink", rootUrl +
  34:                         "ContentPublishingService.ashx"),
  35:                     new XAttribute("blogID", "michael")),
  36:                 new XElement(ns + "api",
  37:                     new XAttribute("name", "Blogger"),
  38:                     new XAttribute("preferred", "false"),
  39:                     new XAttribute("apiLink", rootUrl +
  40:                         "ContentPublishingService.ashx"),
  41:                     new XAttribute("blogID", "michael")))));
  42:  
  43:         context.Response.Write(rsdDocument.ToString());
  44:     }
  45: }

 

I'm using the new LINQ XML support introduced in .NET 3.5 to build the RSD XML document.  The RSD that I am generating tells an RSD client that we support both the MetaWeblog API and the Blogger API, but the MetaWeblog API is preferred over the Blogger API.  The XML that this handler outputs looks like this:

   1: <rsd version="1.0" xmlns="http://archipelago.phrasewise.com/rsd">
   2:   <service>Content Publishing 101</service>
   3:   <engineLine>http://www.sogeti-phoenix.com</engineLine>
   4:   <homePageLink>http://localhost:9500/</homePageLink>
   5:   <apis>
   6:     <api name="MetaWeblog" preferred="true" 
   7:         apiLink="http://localhost:9500/ContentPublishingService.ashx" 
   8:         blogID="michael" />
   9:     <api name="Blogger" preferred="false" 
  10:         apiLink="http://localhost:9500/ContentPublishingService.ashx" 
  11:         blogID="michael" />
  12:   </apis>
  13: </rsd>

 

One thing to note is that I thought that I read someplace that the blogID attribute in the RSD output isn't used, however when I tried this out I discovered that Windows Live Writer does use it to get the blogid value that it will use with the Blogger and MetaWeblog APIs.  This has important ramifications for using Windows Live Writer for your blog or website.

The main issue that will affect your website is if you were to build some sort of multi-blogger experience, or build a website with several subsites, each with their own sets of categories.  The way that Windows Live Writer requires and uses the blogID attribute means that you can't have one global RSD file that works for the entire site.  Each blog or subsite on your website will need to have its own RSD file.

Now that we have told Windows Live Writer how to find our content publishing service using RSD, it's not time that we help Windows Live Writer out by telling it what features that we support.  Windows Live Writer is actually a fully developed editing environment with a lot of features for creating new content to be published up to a website.  However, Windows Live Writer also knows that it can't be everything to everyone.  There are some features that Windows Live Writer does support that some blog engines and content management systems don't support.  So we have to help out Windows Live Writer from our website to tell WLW what features we do support.  We also want to go a step beyond that and help customize Windows Live Writer to give our content authors a few customizations to help them do their jobs of publishing content.  We can do all of this by providing Windows Live Writer with an XML manifest file that tells Windows Live Writer about our blog or website.

During the blog/website registration process with Windows Live Writer, Windows Live Writer will download our RSD file to find the location of the MetaWeblog API.  Windows Live Writer, like Microsoft Office Word 2007, will then make some calls to our website to make sure that the blog exists and to get the list of categories for the website.  Somewhere during the registration process, Windows Live Writer will look at our home page to also find a link to the manifest file for Windows Live Writer.  We will add this link right next to the RSD link in the header section of our home page:

   1: <html xmlns="http://www.w3.org/1999/xhtml">
   2: <head runat="server">
   3:     <title>Untitled Page</title>
   4:     <link rel="EditURI" type="application/rsd+xml" title="RSD"
   5:         href="http://localhost:9500/rsd.ashx" />
   6:     <link rel="wlwmanifest" type="application/wlwmanifest+xml"
   7:         href="http://localhost:9500/wlwmanifest.ashx" />
   8: </head>
   9: <body>
  10:     <form id="form1" runat="server">
  11:     <div>
  12:     
  13:     </div>
  14:     </form>
  15: </body>
  16: </html>

 

On line 6 is the link to our manifest file for Windows Live Writer.  I've chosen once again to implement this as an HTTP handler because at some point in the future I may choose to allow the website administrator to selectively configure which options the website should support.  For example, the website administrator may not want posts that have embedded JavaScript code in them, or <embed> elements.

Before I show you the source code for the HTTP handler and the resulting XML generated by it, you might want to pause and take a look at the manifest file specification for Windows Live Writer.  Here's the source code for my manifest file generator:

   1: public void ProcessRequest(HttpContext context) {
   2:     XNamespace ns = "http://schemas.microsoft.com/wlw/manifest/weblog";
   3:     XDocument xmlDocument = new XDocument(new XElement(ns + "manifest",
   4:         new XElement(ns + "options",
   5:             new XElement(ns + "clientType", "Metaweblog"),
   6:             new XElement(ns + "supportsScripts", "Yes"),
   7:             new XElement(ns + "supportsEmbeds", "Yes")),
   8:         new XElement(ns + "weblog",
   9:             new XElement(ns + "serviceName", "Michael's Blog"),
  10:             new XElement(ns + "homepageLinkText",
  11:                 "View Michael's Blog"),
  12:             new XElement(ns + "adminLinkText",
  13:                 "Manage Michael's Blog"),
  14:             new XElement(ns + "adminUrl",
  15:                 new XCData("http://localhost:9500/Admin")),
  16:             new XElement(ns + "postEditingUrl", new XCData(
  17:                 "http://localhost:9500/EditPost.aspx?postId={post-id}"))),
  18:         new XElement(ns + "views",
  19:             new XElement(ns + "default", "WebLayout"),
  20:             new XElement(ns + "view",
  21:                 new XAttribute("type", "WebLayout"),
  22:                 new XAttribute("src",
  23:                     "http://localhost:9500/WebLayout.aspx")),
  24:             new XElement(ns + "view",
  25:                 new XAttribute("type", "WebPreview"),
  26:                 new XAttribute("src",
  27:                     "http://localhost:9500/WebPreview.aspx")))));
  28:  
  29:     context.Response.Write(xmlDocument.ToString());
  30: }

 

Again, I used the new LINQ XML support to generate the XML for the Windows Live Writer manifest.  Here's what the generated XML looks like:

   1: <manifest xmlns="http://schemas.microsoft.com/wlw/manifest/weblog">
   2:   <options>
   3:     <clientType>Metaweblog</clientType>
   4:     <supportsScripts>Yes</supportsScripts>
   5:     <supportsEmbeds>Yes</supportsEmbeds>
   6:   </options>
   7:   <weblog>
   8:     <serviceName>Michael's Blog</serviceName>
   9:     <homepageLinkText>View Michael's Blog</homepageLinkText>
  10:     <adminLinkText>Manage Michael's Blog</adminLinkText>
  11:     <adminUrl><![CDATA[http://localhost:9500/Admin]]></adminUrl>
  12:     <postEditingUrl><![CDATA[
  13:         http://localhost:9500/EditPost.aspx?postId={post-id}
  14:     ]]></postEditingUrl>
  15:   </weblog>
  16:   <views>
  17:     <default>WebLayout</default>
  18:     <view type="WebLayout" 
  19:         src="http://localhost:9500/WebLayout.aspx" />
  20:     <view type="WebPreview" 
  21:         src="http://localhost:9500/WebPreview.aspx" />
  22:   </views>
  23: </manifest>

 

There are several important things to note from this manifest file, mostly on lines 3, 4, and 5.  First, on line 3, I am saying that my blog or website supports the MetaWeblog API, which I implemented in the earlier posts.  You might want to look again at the manifest specification to see what specific features that I am saying that I am supporting for Windows Live Writer.  On lines 4 and 5, I am extending this feature set to say that I'm going to allow new posts to have embedded JavaScript and embedded objects such as Silverlight or YouTube videos, or SlideShare presentations.

Lines 18 and 20 of the listing above also show that I have defined two preview views for Windows Live Writer to use.  Windows Live Writer provides a true WYSIWYG experience for blog publishing in that WLW tries to allow you to write your blog post in the same format that your blog is rendered in.  For example, I am at the moment writing my blog post in Windows Live Writer, and Windows Live Writer has rendered the editor with the same column limitations, fonts, etc., that the Sogeti-Phoenix.com blog has.  So while I am editing my blog post, I get to actually see what it's going to look like when published to and rendered on my blog.  We'll cover these templates more in a later post, but I just thought that I'd mention them.

At this point, we have everything that we need for Windows Live Writer to connect to our website, autodetect the publishing service, and determine what features to provide to authors who will be writing content for the website.

I hope that the next post on this subject will be another screencast where we'll take a look at our new website publishing interfaces from the perspective of Windows Live Writer instead of Microsoft Office Word 2007.



Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted on February 29, 2008 13:47 by swilliams

XML Web Services provide a simple way to access the API of a piece of software over the Internet. Visual Studio and .NET make it stupid easy especially. However, just because something is easy to use does not mean it is impossible to make something horrible.

First a little background information before I start to rant. The standard way of creating a web service in .NET is to create an asmx file and expose various methods with the [WebMethod] attribute:

[WebMethod]
public Person[] GetAllPeople() {
  return this.datathingy.GetPeople();
}

Let's say that the Person class looks like:

public class Person {
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}

.NET and IIS combine to create a web service endpoint for client applications to use. This endpoint is in an XML dialect known as WSDL and looks something like this:

<s:complextype name="Person">
    <s:sequence>
	<s:element name="Id" type="s:int" minoccurs="1" maxoccurs="1" />
	<s:element name="Name" type="s:string" minoccurs="0" maxoccurs="1" />
	<s:element name="Email" type="s:string" minoccurs="0" maxoccurs="1" />
    </s:sequence>
</s:complextype>

99% of the time, you don't have to deal with the WSDL directly though; the framework takes care of it for you by deserializing the XML into an object that can be accessed similarly to the original Person instance. To consume said web service Visual Studio again makes it very easy:

public Person[] RetrievePeople() {
	PeopleService service = new PeopleService();
	return service.GetAllPeople();
}

All in all this works wonderfully and enables you to build rich functionality out of web services.

The Bad Times happen when you deviate from this path. For example, .NET is perfectly fine with letting you return any Type as the return value of a web method. So, you can return something like a DataSet, or an XmlNode. Why is this bad? I'll let someone else field the DataSet question, but let's take a look at why returning an XmlNode is bad.

Even though .NET provides many libraries to help you parse XML, it is still a painfully dreary task. Let's say you have this kind of node:

<root language="en/us">
	<Person>
		<Id>1</Id>
		<Name>Johnny Johnson</Name>
		<Email>jj@example.com</Email>
	</Person>
</root>

That's relatively straightforward, but still requires effort on your part to turn the XmlNode into something useful:

private Person ConvertXml(XmlNode node) {
    Person p = new Person();
    p.Id = Convert.ToInt32(node.SelectSingleNode("Person/Id").Value);
    p.Name = node.SelectSingleNode("Person/Name").Value;
    p.Email = node.SelectSingleNode("Person/Email").Value;
}

Notice a few glaring problems with that? First, it assumes that there are no XML namespaces that need to be added. This is biggest flaw with SharePoint's web services, it requires you add Five XML namespaces and prefixes to run xpath queries on it. But beyond all that, the code is brittle. What happens if one of the nodes that is returned doesn't have an Email tag? Kablooey. So yes, you could add a bunch of code to check for this:

private Person ConvertXml(XmlNode node) {
    Person p = new Person();
    if (node.SelectSingleNode("Person/Id") != null) {
        try {
            p.Id = Convert.ToInt32(node.SelectSingleNode("Person/Id").Value);
        } catch { } // Oh yeah, this might not be a number...
    }
    if (node.SelectSingleNode("Person/Name") != null) {
        p.Name = node.SelectSingleNode("Person/Name").Value;
    }
    if (node.SelectSingleNode("Person/Email") != null) {
        p.Email = node.SelectSingleNode("Person/Email").Value;
    }
    return p;
}

But now you have a 14 line method that still doesn't include any namespacing that many XML documents require. Tack on another 30 lines for that. Compare this to the four liner back at the top of the page. The kicker is that it still tightly coupled with the back end server. The format of the XML returned c