diff xslt/json.xsl @ 12:191b81b2052b

first version of the xslt transform from xml to json
author cin
date Mon, 09 Apr 2018 06:43:46 +0300
parents 14162f9133a1
children 197a850b1f6f
line wrap: on
line diff
--- a/xslt/json.xsl	Sun Apr 08 23:30:11 2018 +0300
+++ b/xslt/json.xsl	Mon Apr 09 06:43:46 2018 +0300
@@ -1,33 +1,70 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <xsl:stylesheet version="1.0"
 	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
-	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xmlns:exsl="http://exslt.org/common">
 	<xsl:output method="text" />
+	
+	<xsl:key name="members" match="*" use="concat(generate-id(..),local-name(.))"/>
 
-	<xsl:template match="*[count(*) = 0]">
-		<xsl:apply-templates mode="json-value" />
-	</xsl:template>
-
-	<xsl:template match="*[count(*) > 0]">
-		<xsl:text>{ </xsl:text>
-		<xsl:apply-templates mode="json-object-member"/>
-		<xsl:text> }</xsl:text>
+	<xsl:template match="/">
+		<xsl:variable name="doc">
+			<xsl:apply-templates/>
+		</xsl:variable>
+		<xsl:apply-templates select="exsl:node-set($doc)" mode="json-value" />
 	</xsl:template>
 	
-	<xsl:template match="text()"/>
-
+	<xsl:template match="*">
+		<xsl:copy>
+			<xsl:apply-templates select="@*"/>
+			<xsl:apply-templates/>
+		</xsl:copy>
+	</xsl:template>
+	
+	<xsl:template match="@*">
+		<xsl:copy/>
+	</xsl:template>
+	
 	<!-- handle json-object -->
 	
-	<xsl:template name="write-object">
-	
+	<xsl:template match="*" mode="json-object-members">
+		<xsl:apply-templates select="." mode="json-object-members-internal"/>
 	</xsl:template>
-
+	
+	<xsl:template match="*" mode="json-object-members-internal">
+		<xsl:variable name="oid" select="generate-id(.)"/>
+		<xsl:variable name="grouped" select="*[generate-id(.) = generate-id(key('members',concat($oid,local-name(.))))]"/>
+		<xsl:apply-templates select="$grouped" mode="json-object-member"/>
+	</xsl:template>
+	
 	<xsl:template match="*" mode="json-object-member">
-		<xsl:call-template name="write-json-string">
+		<xsl:call-template name="write-string">
 			<xsl:with-param name="text" select="local-name(.)"/>
 		</xsl:call-template>
 		<xsl:text> : </xsl:text>
-		<xsl:apply-templates mode="json-value"/>
+		<xsl:apply-templates select="." mode="json-member-value"/>
+		<xsl:if test="position() != last()">
+			<xsl:text>, </xsl:text>
+		</xsl:if>
+	</xsl:template>
+	
+	<xsl:template match="*" mode="json-member-value">
+		<xsl:variable name="oid" select="generate-id(..)"/>
+		<xsl:variable name="values" select="key('members',concat($oid,local-name(.)))"/>
+		<xsl:choose>
+			<xsl:when test="count($values) > 1">
+				<xsl:call-template name="write-array">
+					<xsl:with-param name="values" select="$values"/>
+				</xsl:call-template>
+			</xsl:when>
+			<xsl:otherwise>
+				<xsl:apply-templates select="$values" mode="json-value"/>
+			</xsl:otherwise>
+		</xsl:choose>
+	</xsl:template>
+	
+	<xsl:template match="*" mode="json-array-item">
+		<xsl:apply-templates select="." mode="json-value"/>
 		<xsl:if test="position() != last()">
 			<xsl:text>, </xsl:text>
 		</xsl:if>
@@ -35,10 +72,6 @@
 	
 	<!-- handle json-value -->
 
-	<xsl:template match="*[@xsi:nil = 'true']">
-		<xsl:text>null</xsl:text>
-	</xsl:template>
-
 	<xsl:template match="text()[. = 'true']" mode="json-value">
 		<xsl:text>true</xsl:text>
 	</xsl:template>
@@ -54,19 +87,64 @@
 	</xsl:template>
 
 	<xsl:template match="text()" mode="json-value">
-		<xsl:call-template name="write-json-string">
+		<xsl:call-template name="write-string">
 			<xsl:with-param name="text" select="."/>
 		</xsl:call-template>
 	</xsl:template>
 
-	<xsl:template match="*" mode="json-value">
-		<xsl:apply-templates select="."/>
+	<xsl:template match="*[boolean(* | @*) or not(text())]" mode="json-value">
+		<xsl:call-template name="write-object"/>
+	</xsl:template>
+	
+	<xsl:template match="*[@xsi:nil = 'true']" mode="json-value">
+		<xsl:text>null</xsl:text>
 	</xsl:template>
 	
 	<!-- template traits -->
-	<xsl:template name="write-json-string">
+	
+	<xsl:template name="write-value">
+		<xsl:param name="value" select="."/>
+		<xsl:apply-templates select="exsl:node-set($value)" mode="json-value"/>
+	</xsl:template>
+	
+	<xsl:template name="write-named-value">
+		<xsl:param name="name"/>
+		<xsl:param name="value"/>
+		<xsl:call-template name="write-string">
+			<xsl:with-param name="text" select="$name"/>
+		</xsl:call-template>
+		<xsl:text> : </xsl:text>
+		<xsl:apply-templates select="exsl:node-set($value)"/>
+	</xsl:template>
+	
+	<!-- specialized template traits -->
+	
+	<xsl:template name="write-string">
 		<xsl:param name="text"/>
 		<xsl:value-of select="concat('&quot;', $text,'&quot;')" />
 	</xsl:template>
+	
+	<xsl:template name="write-object">
+		<xsl:param name="value" select="."/>
+		<xsl:text>{ </xsl:text>
+		<xsl:apply-templates select="$value" mode="json-object-members"/>
+		<xsl:text> }</xsl:text>
+	</xsl:template>
+	
+	<xsl:template name="write-array">
+		<xsl:param name="oid" select="generate-id(..)"/>
+		<xsl:param name="values" select="key('members',concat($oid,local-name(.)))"/>
+		
+		<xsl:text>[ </xsl:text>
+		<xsl:apply-templates select="$values" mode="json-array-item"/>
+		<xsl:text> ]</xsl:text>
+	</xsl:template>
+	
+	<xsl:template name="write-object-members">
+		<xsl:param name="object" select="."/>
+		<xsl:for-each select="exsl:node-set($object)">
+			<xsl:apply-templates select="." mode="json-object-members-internal"/>
+		</xsl:for-each>
+	</xsl:template>
 
 </xsl:stylesheet>
\ No newline at end of file