Skip to content

Instantly share code, notes, and snippets.

@dmj
Created November 14, 2018 07:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dmj/83e1e1cd003d448fa8df2d94e4481537 to your computer and use it in GitHub Desktop.
Save dmj/83e1e1cd003d448fa8df2d94e4481537 to your computer and use it in GitHub Desktop.
JSON-Pointer over canonical XML representation of JSON (just for the fun of it)
<json:map xmlns:json="http://www.w3.org/2005/xpath-functions">
<json:array key="foo">
<json:string>bar</json:string>
<json:string>baz</json:string>
</json:array>
<json:number key="">0</json:number>
<json:number key="a/b">1</json:number>
<json:number key="c%d">2</json:number>
<json:number key="e^f">3</json:number>
<json:number key="g|h">4</json:number>
<json:number key="i\j">5</json:number>
<json:number key="k&#34;l">6</json:number>
<json:number key=" ">7</json:number>
<json:number key="m~n">8</json:number>
</json:map>
<xsl:transform version="3.0"
xmlns:json="http://www.w3.org/2005/xpath-functions"
xmlns:jptr="tag:maus@hab.de,2018:RFC6901"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!--
Returns the selected JSON information item or the empty sequence if the pointer does not denote
an information item in the JSON document. Expects the pointer to be a sequence of reference
tokens, i.e. the result of applying tokenize() on the pointer's string representation.
-->
<xsl:function name="jptr:evaluate" as="element()?">
<xsl:param name="context" as="element()?"/>
<xsl:param name="pointer" as="xs:string*"/>
<xsl:variable name="token" select="jptr:unescape($pointer[1])"/>
<xsl:choose>
<xsl:when test="empty($context)"/>
<xsl:when test="empty($pointer)">
<xsl:sequence select="$context"/>
</xsl:when>
<xsl:when test="$token = ''">
<xsl:sequence select="jptr:evaluate($context, $pointer[position() gt 1])"/>
</xsl:when>
<xsl:when test="$context/self::json:map">
<xsl:sequence select="jptr:evaluate($context/json:*[@key = $token], $pointer[position() gt 1])"/>
</xsl:when>
<xsl:when test="$context/self::json:array and matches($token, '[0-9]+')">
<xsl:sequence select="jptr:evaluate($context/json:*[position() eq (number($token) + 1)], $pointer[position() gt 1])"/>
</xsl:when>
</xsl:choose>
</xsl:function>
<xsl:function name="jptr:unescape" as="xs:string?">
<xsl:param name="token" as="xs:string?"/>
<xsl:value-of select="replace(replace($token, '~1', '/'), '~0', '~')"/>
</xsl:function>
</xsl:transform>
<!DOCTYPE description [<!ENTITY example SYSTEM "rfc6901.xml">]>
<description xmlns="http://www.jenitennison.com/xslt/xspec"
xmlns:xi="http://www.w3.org/2001/XInclude"
xmlns:jptr="tag:maus@hab.de,2018:RFC6901"
xmlns:json="http://www.w3.org/2005/xpath-functions"
stylesheet="rfc6901.xsl">
<scenario label="When jptr:unescape is called">
<scenario label="with a pointer segment containing an encoded forward slash">
<call function="jptr:unescape">
<param name="token" as="xs:string">foo~1bar</param>
</call>
<expect label="the forward slash is unescaped" select="'foo/bar'"/>
</scenario>
<scenario label="with a pointer segment containing an encoded tilde">
<call function="jptr:unescape">
<param name="token" as="xs:string">foo~0bar</param>
</call>
<expect label="the tilde is unescaped" select="'foo~bar'"/>
</scenario>
<scenario label="with a pointer segment containing an encoded tilde followed by a one">
<call function="jptr:unescape">
<param name="token" as="xs:string">~01</param>
</call>
<expect label="only the tilde is unescaped" select="'~1'"/>
</scenario>
</scenario>
<scenario label="When jptr:evaluate is called">
<scenario label="with the empty string as pointer">
<call function="jptr:evaluate">
<param name="context">
&example;
</param>
<param name="pointer"/>
</call>
<expect label="the entire document is returned">
<json:map>...</json:map>
</expect>
</scenario>
<scenario label="with a string denoting the member of a map">
<call function="jptr:evaluate">
<param name="context">
&example;
</param>
<param name="pointer" as="xs:string+" select="tokenize('/foo', '/')"/>
</call>
<expect label="the member with the same name is returned">
<json:array key="foo">...</json:array>
</expect>
</scenario>
<scenario label="with a string not denoting the member of a map">
<call function="jptr:evaluate">
<param name="context">
&example;
</param>
<param name="pointer" as="xs:string+" select="tokenize('/foo/bar', '/')"/>
</call>
<expect label="the empty sequence is returned"/>
</scenario>
<scenario label="with a numeric string denoting an array index">
<call function="jptr:evaluate">
<param name="context">
&example;
</param>
<param name="pointer" as="xs:string+" select="tokenize('/foo/0', '/')"/>
</call>
<expect label="the array member at this position is returned">
<json:string>bar</json:string>
</expect>
</scenario>
<scenario label="with a numeric string not denoting an array index">
<call function="jptr:evaluate">
<param name="context">
&example;
</param>
<param name="pointer" as="xs:string+" select="tokenize('/foo/3', '/')"/>
</call>
<expect label="the empty sequence is returned"/>
</scenario>
</scenario>
</description>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment