This appendix contains the schema for the XML representation of JSON described in 22.1 XML Representation of JSON, together with the stylesheets used for converting from this XML representation to strings matching the JSON grammar.
These schema documents and stylesheets are also available as separate resources (links are listed at the top of this document).
The schema is reproduced below:
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://www.w3.org/2005/xpath-functions" xmlns:j="http://www.w3.org/2005/xpath-functions"> <!-- * This is a schema for the XML representation of JSON used as the target for the * function fn:json-to-xml() * * The schema is made available under the terms of the W3C software notice and license * at http://www.w3.org/Consortium/Legal/copyright-software-19980720 * --> <xs:element name="map" type="j:mapType"> <xs:unique name="unique-key"> <xs:selector xpath="*"/> <xs:field xpath="@key"/> <xs:field xpath="@escaped-key"/> </xs:unique> </xs:element> <xs:element name="array" type="j:arrayType"/> <xs:element name="string" type="j:stringType"/> <xs:element name="number" type="j:numberType"/> <xs:element name="boolean" type="xs:boolean"/> <xs:element name="null" type="j:nullType"/> <xs:complexType name="nullType"> <xs:sequence/> </xs:complexType> <xs:complexType name="stringType"> <xs:simpleContent> <xs:extension base="xs:string"> <xs:attribute name="escaped" type="xs:boolean" use="optional" default="false"/> </xs:extension> </xs:simpleContent> </xs:complexType> <xs:simpleType name="numberType"> <xs:restriction base="xs:double"> <!-- exclude positive and negative infinity, and NaN --> <xs:minExclusive value="-INF"/> <xs:maxExclusive value="INF"/> </xs:restriction> </xs:simpleType> <xs:complexType name="arrayType"> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element ref="j:map"/> <xs:element ref="j:array"/> <xs:element ref="j:string"/> <xs:element ref="j:number"/> <xs:element ref="j:boolean"/> <xs:element ref="j:null"/> </xs:choice> </xs:complexType> <xs:complexType name="mapType"> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="map"> <xs:complexType> <xs:complexContent> <xs:extension base="j:mapType"> <xs:attribute name="key" type="xs:string"/> </xs:extension> </xs:complexContent> </xs:complexType> <xs:unique name="unique-key-2"> <xs:selector xpath="*"/> <xs:field xpath="@key"/> </xs:unique> </xs:element> <xs:element name="array"> <xs:complexType> <xs:complexContent> <xs:extension base="j:arrayType"> <xs:attributeGroup ref="j:key-group"/> </xs:extension> </xs:complexContent> </xs:complexType> </xs:element> <xs:element name="string"> <xs:complexType> <xs:simpleContent> <xs:extension base="j:stringType"> <xs:attributeGroup ref="j:key-group"/> </xs:extension> </xs:simpleContent> </xs:complexType> </xs:element> <xs:element name="number"> <xs:complexType> <xs:simpleContent> <xs:extension base="j:numberType"> <xs:attributeGroup ref="j:key-group"/> </xs:extension> </xs:simpleContent> </xs:complexType> </xs:element> <xs:element name="boolean"> <xs:complexType> <xs:simpleContent> <xs:extension base="xs:boolean"> <xs:attributeGroup ref="j:key-group"/> </xs:extension> </xs:simpleContent> </xs:complexType> </xs:element> <xs:element name="null"> <xs:complexType> <xs:attributeGroup ref="j:key-group"/> </xs:complexType> </xs:element> </xs:choice> </xs:complexType> <xs:attributeGroup name="key-group"> <xs:attribute name="key" type="xs:string" use="required"/> <xs:attribute name="escaped-key" type="xs:boolean" use="optional" default="false"/> </xs:attributeGroup> </xs:schema>
This stylesheet contains the implementation of a function very similar to
xml-to-json
, but implemented in XSLT so that it can be
customized and extended. This stylesheet is provided for the benefit of users and
there are no conformance requirements associated with it; there is no requirement
that processors should make this stylesheet available. The stylesheet is reproduced
below:
<?xml version="1.0" encoding="UTF-8"?> <!-- * This is a stylesheet for converting XML to JSON. * It expects the XML to be in the format produced by the XSLT 3.0 function * fn:json-to-xml(), but is designed to be highly customizable. * * The stylesheet is made available under the terms of the W3C software notice and license * at http://www.w3.org/Consortium/Legal/copyright-software-19980720 * --> <xsl:package name="http://www.w3.org/2013/XSLT/xml-to-json" package-version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:j="http://www.w3.org/2013/XSLT/xml-to-json" exclude-result-prefixes="xs fn j" default-mode="j:xml-to-json" version="3.0"> <xsl:variable name="quot" visibility="private">"</xsl:variable> <xsl:param name="indent-spaces" select="2"/> <!-- The static parameter STREAMABLE controls whether the stylesheet is declared as streamable --> <xsl:param name="STREAMABLE" static="yes" as="xs:boolean" select="true()"/> <xsl:mode name="indent" _streamable="{$STREAMABLE}" visibility="public"/> <xsl:mode name="no-indent" _streamable="{$STREAMABLE}" visibility="public"/> <xsl:mode name="key-attribute" streamable="false" on-no-match="fail" visibility="public"/> <!-- The static parameter VALIDATE controls whether the input, if untyped, should be validated --> <xsl:param name="VALIDATE" static="yes" as="xs:boolean" select="false()"/> <xsl:import-schema namespace="http://www.w3.org/2005/xpath-functions" use-when="$VALIDATE"/> <!-- Entry point: function to convert a supplied XML node to a JSON string --> <xsl:function name="j:xml-to-json" as="xs:string" visibility="public"> <xsl:param name="input" as="node()"/> <xsl:sequence select="j:xml-to-json($input, map{})"/> </xsl:function> <!-- Entry point: function to convert a supplied XML node to a JSON string, supplying options --> <xsl:function name="j:xml-to-json" as="xs:string" visibility="public"> <xsl:param name="input" as="node()"/> <xsl:param name="options" as="map(*)"/> <xsl:variable name="input" as="node()" use-when="$VALIDATE"> <xsl:copy-of select="$input" validation="strict"/> </xsl:variable> <xsl:choose> <xsl:when test="$options('indent') eq true()"> <xsl:apply-templates select="$input" mode="indent"> <xsl:with-param name="fallback" as="(function(element()) as xs:string)?" select="$options('fallback')" tunnel="yes"/> </xsl:apply-templates> </xsl:when> <xsl:otherwise> <xsl:apply-templates select="$input" mode="no-indent"> <xsl:with-param name="fallback" as="(function(element()) as xs:string)?" select="$options('fallback')" tunnel="yes"/> </xsl:apply-templates> </xsl:otherwise> </xsl:choose> </xsl:function> <!-- A document node is ignored --> <xsl:template match="/" mode="indent no-indent"> <xsl:apply-templates mode="#current"/> </xsl:template> <!-- Template rule for fn:map elements, representing JSON objects --> <xsl:template match="fn:map" mode="indent"> <xsl:value-of> <xsl:variable name="depth" select="count(ancestor::*) + 1"/> <xsl:text>{</xsl:text> <xsl:for-each select="*"> <xsl:if test="position() gt 1"> <xsl:text>, </xsl:text> <xsl:value-of select="j:indent($depth)"/> </xsl:if> <xsl:apply-templates select="snapshot(@key)" mode="key-attribute"/> <xsl:text> : </xsl:text> <xsl:apply-templates select="." mode="#current"/> </xsl:for-each> <xsl:text>}</xsl:text> </xsl:value-of> </xsl:template> <xsl:template match="fn:map" mode="no-indent"> <xsl:value-of> <xsl:text>{</xsl:text> <xsl:for-each select="*"> <xsl:if test="position() gt 1"> <xsl:text>,</xsl:text> </xsl:if> <xsl:apply-templates select="snapshot(@key)" mode="key-attribute"/> <xsl:text>:</xsl:text> <xsl:apply-templates select="." mode="#current"/> </xsl:for-each> <xsl:text>}</xsl:text> </xsl:value-of> </xsl:template> <!-- Template rule for fn:array elements, representing JSON arrays --> <xsl:template match="fn:array" mode="indent"> <xsl:value-of> <xsl:variable name="depth" select="count(ancestor::*) + 1"/> <xsl:text>[</xsl:text> <xsl:for-each select="*"> <xsl:if test="position() gt 1"> <xsl:text>, </xsl:text> <xsl:value-of select="j:indent($depth)"/> </xsl:if> <xsl:apply-templates select="." mode="#current"/> </xsl:for-each> <xsl:text>]</xsl:text> </xsl:value-of> </xsl:template> <xsl:template match="fn:array" mode="no-indent"> <xsl:value-of> <xsl:text>[</xsl:text> <xsl:for-each select="*"> <xsl:if test="position() gt 1"> <xsl:text>,</xsl:text> </xsl:if> <xsl:apply-templates select="." mode="#current"/> </xsl:for-each> <xsl:text>]</xsl:text> </xsl:value-of> </xsl:template> <!-- Template rule for fn:string elements in which special characters are already escaped --> <xsl:template match="fn:string[@escaped='true']" mode="indent no-indent"> <xsl:sequence select="concat($quot, ., $quot)"/> </xsl:template> <!-- Template rule for fn:string elements in which special characters need to be escaped --> <xsl:template match="fn:string[not(@escaped='true')]" mode="indent no-indent"> <xsl:sequence select="concat($quot, j:escape(.), $quot)"/> </xsl:template> <!-- Template rule for fn:boolean elements --> <xsl:template match="fn:boolean" mode="indent no-indent"> <xsl:sequence select="xs:string(xs:boolean(.))"/> </xsl:template> <!-- Template rule for fn:number elements --> <xsl:template match="fn:number" mode="indent no-indent"> <xsl:value-of select="xs:string(xs:double(.))"/> </xsl:template> <!-- Template rule for JSON null elements --> <xsl:template match="fn:null" mode="indent no-indent"> <xsl:text>null</xsl:text> </xsl:template> <!-- Template rule matching a key within a map where special characters in the key are already escaped --> <xsl:template match="fn:*[@key-escaped='true']/@key" mode="key-attribute"> <xsl:value-of select="concat($quot, ., $quot)"/> </xsl:template> <!-- Template rule matching a key within a map where special characters in the key need to be escaped --> <xsl:template match="fn:*[not(@key-escaped='true')]/@key" mode="key-attribute"> <xsl:value-of select="concat($quot, j:escape(.), $quot)"/> </xsl:template> <!-- Template matching "invalid" elements --> <xsl:template match="*" mode="indent no-indent"> <xsl:param name="fallback" as="(function(element()) as xs:string)?" tunnel="yes" required="yes"/> <xsl:choose> <xsl:when test="exists($fallback)"> <xsl:value-of select="$fallback(snapshot(.))"/> </xsl:when> <xsl:otherwise> <xsl:message terminate="yes">>Inc</xsl:message> </xsl:otherwise> </xsl:choose> </xsl:template> <!-- Template rule matching (and discarding) whitespace text nodes in the XML --> <xsl:template match="text()[not(normalize-space())]" mode="indent no-indent"/> <!-- Function to escape special characters --> <xsl:function name="j:escape" as="xs:string" visibility="final"> <xsl:param name="in" as="xs:string"/> <xsl:value-of> <xsl:for-each select="string-to-codepoints($in)"> <xsl:choose> <xsl:when test=". gt 65535"> <xsl:value-of select="concat('\u', j:hex4((. - 65536) idiv 1024 + 55296))"/> <xsl:value-of select="concat('\u', j:hex4((. - 65536) mod 1024 + 56320))"/> </xsl:when> <xsl:when test=". = 34">\"</xsl:when> <xsl:when test=". = 92">\\</xsl:when> <xsl:when test=". = 08">\b</xsl:when> <xsl:when test=". = 09">\t</xsl:when> <xsl:when test=". = 10">\n</xsl:when> <xsl:when test=". = 12">\f</xsl:when> <xsl:when test=". = 13">\r</xsl:when> <xsl:when test=". lt 32 or (. ge 127 and . le 160)"> <xsl:value-of select="concat('\u', j:hex4(.))"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="codepoints-to-string(.)"/> </xsl:otherwise> </xsl:choose> </xsl:for-each> </xsl:value-of> </xsl:function> <!-- Function to convert a UTF16 codepoint into a string of four hex digits --> <xsl:function name="j:hex4" as="xs:string" visibility="final"> <xsl:param name="ch" as="xs:integer"/> <xsl:variable name="hex" select="'0123456789abcdef'"/> <xsl:value-of> <xsl:value-of select="substring($hex, $ch idiv 4096 + 1, 1)"/> <xsl:value-of select="substring($hex, $ch idiv 256 mod 16 + 1, 1)"/> <xsl:value-of select="substring($hex, $ch idiv 16 mod 16 + 1, 1)"/> <xsl:value-of select="substring($hex, $ch mod 16 + 1, 1)"/> </xsl:value-of> </xsl:function> <!-- Function to output whitespace indentation based on the depth of the node supplied as a parameter --> <xsl:function name="j:indent" as="text()" visibility="public"> <xsl:param name="depth" as="xs:integer"/> <xsl:value-of select="'
', string-join((1 to ($depth + 1) * $indent-spaces) ! ' ', '')"/> </xsl:function> </xsl:package>