Created
May 10, 2012 14:42
-
-
Save xpathr/2653562 to your computer and use it in GitHub Desktop.
XML to JSON by vladG
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<xsl:stylesheet version="1.0" | |
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" | |
xmlns:exsl="http://exslt.org/common" | |
extension-element-prefixes="exsl"> | |
<!-- | |
A complete XML to JSON transformation utility adapted from | |
Doeke Zanstra's xml2json utility (http://code.google.com/p/xml2json-xslt/) | |
This version: | |
- resorbs* repetitive elements: | |
<days> | |
<day>Monday</day> | |
<day>Tuesday</day> | |
<day>Wendsday</day> | |
</days> | |
====> {days: ["Monday", "Tuesday", "Wendsday"] } | |
*Nota bene: If only 1 element exists, it will be output as-is | |
<days> | |
<day>Monday</day> | |
</days> | |
====> {days: {day: "Monday"} } | |
- empty text nodes are transformed to empty strings | |
<days> | |
<day></day> | |
<day>Monday</day> | |
</days> | |
====> {days: ["", "Monday"] } | |
- numbers are output as integers | |
- nodes with text equal to "null" are transformed to items with value set to Javascript `null` | |
<days> | |
null | |
</days> | |
====> {days: null } | |
--> | |
<xsl:strip-space elements="*"/> | |
<!-- | |
Example call | |
<xsl:call-template name="xml-to-json"> | |
<xsl:with-param name="xml"> | |
[Any XML or XSLT transformation] | |
</xsl:with-param> | |
</xsl:call-template> | |
--> | |
<xsl:template name="xml-to-json"> | |
<xsl:param name="xml"/> | |
<xsl:apply-templates select="exsl:node-set($xml)" mode="xml-to-json"/> | |
</xsl:template> | |
<!-- string --> | |
<xsl:template match="text()" mode="xml-to-json" > | |
<xsl:choose> | |
<xsl:when test=". = 'null'">null</xsl:when> | |
<xsl:otherwise> | |
<xsl:call-template name="escape-string"> | |
<xsl:with-param name="s" select="."/> | |
</xsl:call-template> | |
</xsl:otherwise> | |
</xsl:choose> | |
</xsl:template> | |
<!-- Main template for escaping strings; used by above template and for object-properties | |
Responsibilities: placed quotes around string, and chain up to next filter, escape-bs-string --> | |
<xsl:template name="escape-string"> | |
<xsl:param name="s"/> | |
<xsl:text>"</xsl:text> | |
<xsl:call-template name="escape-bs-string"> | |
<xsl:with-param name="s" select="$s"/> | |
</xsl:call-template> | |
<xsl:text>"</xsl:text> | |
</xsl:template> | |
<!-- Escape the backslash (\) before everything else. --> | |
<xsl:template name="escape-bs-string"> | |
<xsl:param name="s"/> | |
<xsl:choose> | |
<xsl:when test="contains($s,'\')"> | |
<xsl:call-template name="escape-quot-string"> | |
<xsl:with-param name="s" select="concat(substring-before($s,'\'),'\\')"/> | |
</xsl:call-template> | |
<xsl:call-template name="escape-bs-string"> | |
<xsl:with-param name="s" select="substring-after($s,'\')"/> | |
</xsl:call-template> | |
</xsl:when> | |
<xsl:otherwise> | |
<xsl:call-template name="escape-quot-string"> | |
<xsl:with-param name="s" select="$s"/> | |
</xsl:call-template> | |
</xsl:otherwise> | |
</xsl:choose> | |
</xsl:template> | |
<!-- Escape the double quote ("). --> | |
<xsl:template name="escape-quot-string"> | |
<xsl:param name="s"/> | |
<xsl:choose> | |
<xsl:when test="contains($s,'"')"> | |
<xsl:call-template name="encode-string"> | |
<xsl:with-param name="s" select="concat(substring-before($s,'"'),'\"')"/> | |
</xsl:call-template> | |
<xsl:call-template name="escape-quot-string"> | |
<xsl:with-param name="s" select="substring-after($s,'"')"/> | |
</xsl:call-template> | |
</xsl:when> | |
<xsl:otherwise> | |
<xsl:call-template name="encode-string"> | |
<xsl:with-param name="s" select="$s"/> | |
</xsl:call-template> | |
</xsl:otherwise> | |
</xsl:choose> | |
</xsl:template> | |
<!-- Replace tab, line feed and/or carriage return by its matching escape code. Can't escape backslash | |
or double quote here, because they don't replace characters (� becomes \t), but they prefix | |
characters (\ becomes \\). Besides, backslash should be seperate anyway, because it should be | |
processed first. This function can't do that. --> | |
<xsl:template name="encode-string"> | |
<xsl:param name="s"/> | |
<xsl:choose> | |
<!-- tab --> | |
<xsl:when test="contains($s,'	')"> | |
<xsl:call-template name="encode-string"> | |
<xsl:with-param name="s" select="concat(substring-before($s,'	'),'\t',substring-after($s,'	'))"/> | |
</xsl:call-template> | |
</xsl:when> | |
<!-- line feed --> | |
<xsl:when test="contains($s,'
')"> | |
<xsl:call-template name="encode-string"> | |
<xsl:with-param name="s" select="concat(substring-before($s,'
'),'\n',substring-after($s,'
'))"/> | |
</xsl:call-template> | |
</xsl:when> | |
<!-- carriage return --> | |
<xsl:when test="contains($s,'
')"> | |
<xsl:call-template name="encode-string"> | |
<xsl:with-param name="s" select="concat(substring-before($s,'
'),'\r',substring-after($s,'
'))"/> | |
</xsl:call-template> | |
</xsl:when> | |
<xsl:otherwise><xsl:value-of select="$s"/></xsl:otherwise> | |
</xsl:choose> | |
</xsl:template> | |
<!-- number (no support for javascript mantise) --> | |
<xsl:template match="text()[not(string(number())='NaN')]" mode="xml-to-json" > | |
<xsl:value-of select="."/> | |
</xsl:template> | |
<!-- boolean, case-insensitive --> | |
<xsl:template match="text()[translate(.,'TRUE','true')='true']" mode="xml-to-json" >true</xsl:template> | |
<xsl:template match="text()[translate(.,'FALSE','false')='false']" mode="xml-to-json" >false</xsl:template> | |
<!-- object --> | |
<xsl:template match="*" mode="xml-to-json" > | |
<xsl:if test="not(preceding-sibling::*)">{</xsl:if> | |
<xsl:call-template name="escape-string"> | |
<xsl:with-param name="s" select="name()"/> | |
</xsl:call-template> | |
<xsl:text>:</xsl:text> | |
<xsl:if test="count(child::node())=0">""</xsl:if> | |
<xsl:apply-templates select="child::node()" mode="xml-to-json"/> | |
<xsl:if test="following-sibling::*">,</xsl:if> | |
<xsl:if test="not(following-sibling::*)">}</xsl:if> | |
</xsl:template> | |
<!-- array --> | |
<xsl:template match="*[count(../*[name(../*)=name(.)])=count(../*) and count(../*)>1]" mode="xml-to-json" > | |
<xsl:if test="not(preceding-sibling::*)">[</xsl:if> | |
<xsl:choose> | |
<xsl:when test="not(child::node())"> | |
<xsl:text>null</xsl:text> | |
</xsl:when> | |
<xsl:otherwise> | |
<xsl:apply-templates select="child::node()" mode="xml-to-json"/> | |
</xsl:otherwise> | |
</xsl:choose> | |
<xsl:if test="following-sibling::*">,</xsl:if> | |
<xsl:if test="not(following-sibling::*)">]</xsl:if> | |
</xsl:template> | |
</xsl:stylesheet> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks a lot this was really helpful!