Give me JSON

As a frontend developer I love Umbraco because of the fact that I can get a very long way without ever touching any .NET code. In the last year or so I have been doing more and more work with AJAX, be it singlepage apps or just parts of a website that loads content asynchronously.

After loading content, I usually want to render it as HTML on the page. To do this, I create an altTemplate (see Matt's previous post about altTemplates, if you want to know more) and insert an XSLT macro on that page.

In the past I usually had this macro render all the HTML I needed. Then when I requested the altTemplate, I could just inject the contents into the page like this:

$.ajax({
	url: "alttemplateUrl",
	dataType: "html",
	success: function(html){
		$("#insertDataInThisElement").html(html);
	}
});

And voila! New content is rendered on the page. This works great when you are working with smaller chunks of HTML and don't need to use data from the request that's not intended to be rendered.

So what can we do if we want something that's easier to work with in JavaScript than just an HTML string? We use JSON of course! But hey, how do I get Umbraco to output JSON without having to do any .NET work? If you are an XSLT man like I am, you simply install Peter Duncanson's brilliant package XSLToJSON (Which also works in Umbraco 4.11.1)

The package creates a toJSON.xslt macro and a JSON template. The XSLT macro outputs $currentpage as JSON, like this:

<xsl:template match="/">
	<xsl:value-of select="orc.XSLToJSON:XmlToJson($currentPage)" />
</xsl:template>

See it in action by going to yourwebsite.com/JSON to see what it outputs.

You could extend this template to output a specific page by doing something like this: 

<xsl:variable name="pageId" select="umbraco.library:Request('pageid')" />

<xsl:template match="/">
	<!-- Make sure pageId is not empty -->
	<xsl:if test="normalize-space($pageId)">
		<!-- Get the page -->
		<xsl:variable name="page" select="umbraco.library:GetXmlNodeById($pageId)" />

		<!-- Output the page as JSON -->
		<xsl:value-of select="orc.XSLToJSON:XmlToJson($page)" />
	</xsl:if>
</xsl:template>

Then you could get a specific page via JavaScript like this:

$.ajax({
	url: "/JSON?pageId=1048",
	dataType: JSON,
	success: function(data){
		// Do something with the data
	}
});

Now we could use a templating library such as handlebars.js to render the content on an HTML page.

If we do something like the example above, there is a good chance we won't need all the data from $currentPage. Suppose we just need the title, bodyText and an image, we don't want to use precious bandwidth to load unnecessary content. So how can we get around that? We construct our own XML and pass it to the XmlToJson() extension like this:

<xsl:variable name="pageId" select="umbraco.library:Request('pageid')" />

<xsl:template match="/">
	<!-- Make sure pageId is not empty -->
	<xsl:if test="normalize-space($pageId)">
		<!-- Get the page -->
		<xsl:variable name="page" select="umbraco.library:GetXmlNodeById($pageId)" />

		<xsl:variable name="customXml">
			<root>
				<!-- We use normalize-space() in the apply-templates statement to ensure we only get elements that have content -->
				<xsl:apply-templates select="$page/title[normalize-space()]" />
				<xsl:apply-templates select="$page/bodyText[normalize-space()]" />
				<xsl:apply-templates select="$page/image[normalize-space()]" />
			</root>
		</xsl:variable>

		<!-- Output the page as JSON -->
		<!-- Because we constructed the xml ourselves we need to use msxml:node-set() to convert it to a nodeset -->
		<xsl:value-of select="orc.XSLToJSON:XmlToJson(msxml:node-set($customXml))" />
	</xsl:if>
</xsl:template>

<xsl:template match="title">
	<title>
		<xsl:value-of select="." />
	</title>
</xsl:template>

<xsl:template match="bodyText">
	<bodyText>
		<xsl:value-of select="." disable-output-escaping="yes"/>
	</bodyText>
</xsl:template>

<xsl:template match="image">
	<xsl:variable name="imageXml" select="umbraco.library:GetMedia(., 0)" />

	<!-- It wouldn't be of much use if we only returned the media id, so we get the url -->
	<imageUrl>
		<xsl:value-of select="$imageXml/umbracoFile" />
	</imageUrl>
</xsl:template>

Now when we request the altTemplate with a pageid as parameter, like this: yourwebsite.com/alttemplate?pageid=1048, we only get the title, bodyText and imageUrl.

Here's the JSON output from a random page I created: 

{
   "root":{
      "title":"OMG! This is JSON",
      "bodyText":"Umbraco modules encapsulate specific bits of advanced functionality that are easily added to your website.",
      "imageUrl":"/media/35/bender_bending_rodriguez.jpg"
   }
}

And that's how you can get JSON data from an altTemplate with the use of the XSLToJSON package.

I'm no Razor expert, but I'm sure you can do something similar there.

That's it, now go make something awesome with JSON from Umbraco :)

Happy holidays!

Thor Madsen-Holm

Thor is on Twitter as