Request for comments on this proposal for APML-JSON. Cheers.
By David Novakovic and Ben Novakovic
APML JSON
Rationale
As
more and more web applications become APML aware there will be services
delivering "APML-Like" data as JSON, often converted directly from APML
itself. This document seeks to outline a sane specification for
JSON-APML to take the guess work out of it for developers seeking to do
work with APML in Javascript, or even seeking a lightweight alternative
to APML itself. Henceforth in this document the original APML spec will
be referred to as "APML" and the new JSON specification will be
referred to as "APML-JSON".
For the most part this
specification will mirror the original APML specification as closely as
possible. The largest difference is that where a NodeList of
Concept/Source nodes with "key" attributes exist in the APML spec now
are replaced with keyword indexed values in a JSON associative array.
This will become clearer in the example.
Another
relatively large difference is the use of camelCase for keys that are
capitalised in the XML nodes, rather than all caps. This is a
compromise between the reccomendation of W3 for using no caps with
underscores and the APML all caps way. Naturally people will do what
they want. ;) Additionally, with the JSON version, if fields are
missing, they are just assumed to be empty, this saves having to
transmit redundant markup.
Examples
To see an example of javascript using JSON with this notation, head
over to Ben Novakovic\\47s example at http://bmn.name/examples/apml/ .
Look at the source to see the javascript.
APML-JSON Breakdown
Basically the root element contains two main parts, the head and the body:
{
"head":{},
"body":{}
}
The head contains some metadata:
"head": {
"version": 0.6,
"title": "Example APML-JSON file for apml.org",
"generator": "Written by Hand",
"userEmail": "sam...@apml.org",
"dateCreated": "2007-03-11T01:55:00Z"
}
Then the body contains some profiles and also has a default profile:
"body": {
"defaultProfile": "Work",
"profiles": {}
}
Each profile contains explicitdata and implicitdata
"profiles": {
"work": {
"implicitData": {},
"explicitData": {}
}
"home": {
"implicitData": {},
"explicitData": {}
}
}
Explicit
data and implicit data are similar with slightly different attribute,
so we\\47ll look aty implicit ones here. There will be examples of
others in the example APML-JSON file that will be at the end of this
documents. Data can be in the form of sources or concepts.
"implicitData": {
"concepts": {
"attention": {
"value": "1.0",
"from": "",
"updated": "2007-03-11T01:55:00Z"
},
"content distribution": {
"value": "1.0",
"from": "GatheringTool.com",
"updated": "2007-03-11T01:55:00Z"
}
},
"sources": {
"http://feeds.feedburner.com/apmlspec": {
"name": "APML.org",
"value": "1.0",
"type": "application/rss+xml",
"value": "0.4"
}
}
}
Sources can also contain an Author.
"http://feeds.feedburner.com/apmlspec": {
"name": "APML.org",
"value": "1.00",
"type": "application/rss+xml",
"value": "0.4"
"author": {
"key": "Sample",
"value": "0.5",
"from": "GatheringTool.com",
"updated": "2007-03-11T01:55:00Z"
}
}
So here we have a full example:
{
"head": {
"version": "0.6",
"title": "Example APML-JSON file for apml.org",
"generator": "Written by Hand",
"userEmail": "sample@apml.org",
"dateCreated": "2007-03-11T01:55:00Z"
},
"body": {
"defaultProfile": "Work",
"profiles": {
"work": {
"implicitData": {
"concepts": {
"attention": {
"value": 0.1,
"from": "Sometool.com",
"updated": "2007-03-11T01:55:00Z"
},
"content distribution": {
"value": 0.1,
"from": "Sometool.com",
"updated": "2007-03-11T01:55:00Z"
}
},
"sources": {
"http://feeds.feedburner.com/apmlspec": {
"name": "APML.org",
"value": 1.00,
"type": "application/rss+xml"
"authors": {
"Sample":{
"value": 0.5,
"from": "GatheringTool.com",
"updated": "2007-03-11T01:55:00Z"
}
}
}
}
},
"explicitData": {
"concepts": {
"attention": {
"value": 0.1
},
"content distribution": {
"value": 0.1,
}
},
"sources": {
"http://feeds.feedburner.com/apmlspec": {
"name": "APML.org",
"value": "1.00",
"type": "application/rss+xml"
"authors": {
"Sample": {
"value": 0.5,
}
}
}
}
}
},
"home": {
"implicitData": {},
"explicitData": {}
}
},
"applications": {
"sample.com": {
"sampleAppEI": {}
}
}
}
XSLT for APML to APML-JSON
apml2json.xslt
\\46lt;xsl:stylesheet version = \\46#39;1.0\\46#39;
xmlns:xsl=\\47http://www.w3.org/1999/XSL/Transform\\47
xmlns:a="http://www.apml.org/apml-0.6"\\46gt;
\\46lt;xsl:output
method = "text"
omit-xml-declaration = "yes"
standalone = "yes"
/\\46gt;
\\46lt;!--
A stylesheet to convert APML-XML data to APML-JSON data.
For info on APML, visit http://apml.org
These templates are very straightfoward and indented to help people read them.
As a consequence of readability, there is lots of whitespace in the results,
normalise and refactor brutally if you use this in production.
Additionally these simple templates will be easily modified to suit the
upcoming APML v1.0 specification.
If you wish to deal with application specific data add a template like:
\\46lt;xsl:template match="a:Application"\\46gt;
handle application data
\\46lt;/xsl:template\\46gt;
Please email davidnovakovic _ at _ gmail.com with bugs/patches/fixes.
This code is contributed "as is" courtesy of comvine Pty Ltd.
--\\46gt;
\\46lt;xsl:template match="/"\\46gt;
{
\\47head\\47:{
\\46#39;version\\46#39;: \\46#39;\\46lt;xsl:value-of select="/a:APML/@version"/\\46gt;\\46#39;,
\\46lt;xsl:for-each select="/a:APML/a:Head/*"\\46gt;
\\46#39;\\46lt;xsl:value-of select="name()" /\\46gt;\\46#39;:\\46#39;\\46lt;xsl:value-of select="."/\\46gt;\\46#39;,
\\46lt;/xsl:for-each\\46gt;
},
\\47body\\47:{
\\46#39;defaultProfile\\46#39;: \\46#39;\\46lt;xsl:value-of select="/a:APML/a:Body/@defaultprofile"/\\46gt;\\46#39;,
\\46lt;xsl:if test="count(/a:APML/a:Body/a:Profile) \46gt; 0"\\46gt;
\\47profiles\\47:{
\\46lt;xsl:for-each select="/a:APML/a:Body"\\46gt;
\\46lt;xsl:apply-templates select="a:Profile"/\\46gt;
\\46lt;/xsl:for-each\\46gt;
},
\\46lt;/xsl:if\\46gt;
\\46lt;xsl:if test="count(/a:APML/a:Body/a:Applications) \46gt; 0"\\46gt;
\\47applications\\47:{
\\46lt;xsl:for-each select="/a:APML/a:Body/a:Applications"\\46gt;
\\46lt;xsl:apply-templates select="a:Application"/\\46gt;
\\46lt;/xsl:for-each\\46gt;
}
\\46lt;/xsl:if\\46gt;
}
}
\\46lt;/xsl:template\\46gt;
\\46lt;!-- This template matches Profile Nodes, this is where we handle
implicit and explicit data --\\46gt;
\\46lt;xsl:template match="a:Profile"\\46gt;
\\46#39;\\46lt;xsl:value-of select="@name"/\\46gt;\\46#39;:{
\\47implicitData\\47:{
\\46lt;xsl:for-each select="a:ImplicitData/*"\\46gt;
\\46lt;xsl:if test="name() = \\46#39;Concepts\\46#39;"\\46gt;
"concepts":{
\\46lt;xsl:for-each select="a:Concept"\\46gt;
\\46#39;\\46lt;xsl:value-of select="@key" /\\46gt;\\46#39;:{
\\46#39;value\\46#39;:\\46lt;xsl:value-of select="@value" /\\46gt;,
\\46#39;from\\46#39;:\\46#39;\\46lt;xsl:value-of select="@from" /\\46gt;\\46#39;,
\\46#39;updated\\46#39;:\\46#39;\\46lt;xsl:value-of select="@updated" /\\46gt;\\46#39;
},
\\46lt;/xsl:for-each\\46gt;
},
\\46lt;/xsl:if\\46gt;
\\46lt;xsl:if test="name() = \\46#39;Sources\\46#39;"\\46gt;
"sources":{
\\46lt;xsl:for-each select="a:Source"\\46gt;
\\46#39;\\46lt;xsl:value-of select="@key" /\\46gt;\\46#39;:{
\\46#39;name\\46#39;:\\46#39;\\46lt;xsl:value-of select="@name" /\\46gt;\\46#39;,
\\46#39;value\\46#39;:\\46lt;xsl:value-of select="@value" /\\46gt;,
\\46#39;type\\46#39;:\\46#39;\\46lt;xsl:value-of select="@type" /\\46gt;\\46#39;,
\\46#39;from\\46#39;:\\46#39;\\46lt;xsl:value-of select="@from" /\\46gt;\\46#39;,
\\46#39;updated\\46#39;:\\46#39;\\46lt;xsl:value-of select="@updated" /\\46gt;\\46#39;,
\\46lt;xsl:if test="count(a:Author) \46gt; 0"\\46gt;
\\47authors\\47: {
\\46lt;xsl:for-each select="a:Author"\\46gt;
\\46#39;\\46lt;xsl:value-of select="@key"/\\46gt;\\46#39;:{
\\46#39;value\\46#39;:\\46lt;xsl:value-of select="@value"/\\46gt;,
\\46#39;from\\46#39;:\\46#39;\\46lt;xsl:value-of select="@from"/\\46gt;\\46#39;,
\\46#39;updated\\46#39;:\\46#39;\\46lt;xsl:value-of select="@updated"/\\46gt;\\46#39;
},
\\46lt;/xsl:for-each\\46gt;
}
\\46lt;/xsl:if\\46gt;
},
\\46lt;/xsl:for-each\\46gt;
}
\\46lt;/xsl:if\\46gt;
\\46lt;/xsl:for-each\\46gt;
},
\\47explicitData\\47:{
\\46lt;xsl:for-each select="a:ExplicitData/*"\\46gt;
\\46lt;xsl:if test="name() = \\46#39;Concepts\\46#39;"\\46gt;
"concepts":{
\\46lt;xsl:for-each select="a:Concept"\\46gt;
\\46#39;\\46lt;xsl:value-of select="@key" /\\46gt;\\46#39;:{
\\46#39;value\\46#39;:\\46lt;xsl:value-of select="@value" /\\46gt;,
},
\\46lt;/xsl:for-each\\46gt;
},
\\46lt;/xsl:if\\46gt;
\\46lt;xsl:if test="name() = \\46#39;Sources\\46#39;"\\46gt;
"sources":{
\\46lt;xsl:for-each select="a:Source"\\46gt;
\\46#39;\\46lt;xsl:value-of select="@key" /\\46gt;\\46#39;:{
\\46#39;name\\46#39;:\\46#39;\\46lt;xsl:value-of select="@name" /\\46gt;\\46#39;,
\\46#39;value\\46#39;:\\46lt;xsl:value-of select="@value" /\\46gt;,
\\46#39;type\\46#39;:\\46#39;\\46lt;xsl:value-of select="@type" /\\46gt;\\46#39;,
\\46lt;xsl:if test="count(a:Author) \46gt; 0"\\46gt;
\\47authors\\47: {
\\46lt;xsl:for-each select="a:Author"\\46gt;
\\46#39;\\46lt;xsl:value-of select="@key"/\\46gt;\\46#39;:{
\\46#39;value\\46#39;:\\46lt;xsl:value-of select="@value"/\\46gt;,
},
\\46lt;/xsl:for-each\\46gt;
}
\\46lt;/xsl:if\\46gt;
},
\\46lt;/xsl:for-each\\46gt;
}
\\46lt;/xsl:if\\46gt;
\\46lt;/xsl:for-each\\46gt;
}
},
\\46lt;/xsl:template\\46gt;
\\46lt;xsl:template match="rmv_white"\\46gt;
\\46lt;/xsl:template\\46gt;
\\46lt;/xsl:stylesheet\\46gt;