2012-08-08 72 views
1

编辑:我有一个解决方案,但我相信有更好的方法。请看下面。XSLT基于逗号分隔的字符串创建多个元素块

源XML:

<?xml version="1.0"?> 
<reservations> 
    <reservation> 
    <id>1</id> 
    <guestId>1111</guestId> 
    <!-- other fields --> 
    </reservation> 
    <reservation> 
    <id>2</id> 
    <guestId>2222,3333,4444</guestId> 
    <!-- other fields --> 
    </reservation> 
</reservations> 

预期输出:

<?xml version="1.0" encoding="UTF-8"?> 
<reservations> 
    <reservation> 
    <id>1</id> 
    <csvGuestString>1111</csvGuestString> 
    <guestId>1111</guestId> 
    <!-- other fields --> 
    </reservation> 
    <reservation> 
    <id>2</id> 
    <csvGuestString>2222,3333,4444</csvGuestString> 
    <guestId>2222</guestId> 
    <!-- other fields --> 
    </reservation> 
    <reservation> 
    <id>2</id> 
    <csvGuestString>2222,3333,4444</csvGuestString> 
    <guestId>3333</guestId> 
    <!-- other fields --> 
    </reservation> 
    <reservation> 
    <id>2</id> 
    <csvGuestString>2222,3333,4444</csvGuestString> 
    <guestId>4444</guestId> 
    <!-- other fields --> 
    </reservation> 
</reservations> 

规则:

  1. 对于<reservation>元素公顷如果客户(由<guestId>中的逗号分隔值定义),则每次都使用下一个guestId值复制该元素 - 以及其后代 - n次。
  2. 原始的<guestId>元素的值必须保留,并且必须放置在新元素<csvGuestString>中。
  3. 必须在XSLT 1.0中完成。
  4. 完美合理地使用EXSLT进行标记。

我有什么到目前为止(它的工作原理,但如果它是不知道的最有效的解决方案):

<?xml version="1.0"?> 
<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:exsl="http://exslt.org/common" 
    exclude-result-prefixes="exsl" 
    version="1.0"> 

    <xsl:output method="xml" omit-xml-declaration="no" indent="yes"/> 
    <xsl:strip-space elements="*"/> 

    <xsl:variable name="vTokenName" select="'token'"/> 
    <xsl:variable name="vDoc" select="/"/> 

    <xsl:template match="node()|@*"> 
    <xsl:copy> 
     <xsl:apply-templates select="node()|@*"/> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template match="guestId"> 
    <csvGuestString> 
     <xsl:apply-templates /> 
    </csvGuestString> 
    </xsl:template> 

    <xsl:template match="reservation"> 
    <xsl:variable name="vGuestRtfPass1"> 
     <xsl:call-template name="tokenize"> 
     <xsl:with-param name="text" select="guestId"/> 
     <xsl:with-param name="delimiter" select="','"/> 
     </xsl:call-template> 
    </xsl:variable> 

    <xsl:apply-templates select="exsl:node-set($vGuestRtfPass1)/*" mode="pass2"> 
     <xsl:with-param name="pPosition" select="position()"/> 
    </xsl:apply-templates> 
    </xsl:template> 

    <xsl:template match="token" mode="pass2"> 
    <xsl:param name="pPosition" /> 

    <reservation> 
     <xsl:apply-templates select="$vDoc/*/reservation[$pPosition]/*" /> 
     <guestId> 
      <xsl:apply-templates /> 
     </guestId> 
    </reservation> 
    </xsl:template> 

    <xsl:template name="tokenize"> 
    <xsl:param name="text"/> 
    <xsl:param name="delimiter" select="' '"/> 
    <xsl:choose> 
     <xsl:when test="contains($text,$delimiter)"> 
     <xsl:element name="{$vTokenName}"> 
      <xsl:value-of select="substring-before($text,$delimiter)"/> 
     </xsl:element> 
     <xsl:call-template name="tokenize"> 
      <xsl:with-param name="text" select="substring-after($text,$delimiter)"/> 
      <xsl:with-param name="delimiter" select="$delimiter"/> 
     </xsl:call-template> 
     </xsl:when> 
     <xsl:when test="$text"> 
     <xsl:element name="{$vTokenName}"> 
      <xsl:value-of select="$text"/> 
     </xsl:element> 
     </xsl:when> 
    </xsl:choose> 
    </xsl:template> 

</xsl:stylesheet> 

和往常一样,感谢您的帮助。

+0

@ColinD - 我目前工作的一个解决方案使用命名的标记化模板将CSV字符串分开,在这些片段上运行标识模板,但以这种方式每个原始的''元素被复制。我的意图是在这里问这个问题,看看有人能在我做之前想出答案。我应该首先到达那里,我会发布;我会津津乐道这个机会,看看我的解决方案是否可以提高效率。 – ABach 2012-08-08 17:02:18

+0

@ColinD - 我已经添加了我到目前为止所提出的。 – ABach 2012-08-08 17:20:15

回答

2

这是没有必要使用任何tokenize()扩展功能,并且此转换可以在仅具有xxx:node-set()扩展功能的XSLT处理器上运行:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 
<xsl:strip-space elements="*"/> 

<xsl:template match="@*|node()" name="identity"> 
    <xsl:copy> 
    <xsl:apply-templates select="@*|node()"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="reservation/guestId"> 
    <xsl:call-template name="identity"/> 
    <csvGuestString><xsl:value-of select="."/></csvGuestString> 
</xsl:template> 

<xsl:template match="reservation[contains(guestId, ',')]" name="explode"> 
    <xsl:param name="pCurrent" select="."/> 
    <xsl:param name="pLastId" select="substring-before($pCurrent/guestId, ',')"/> 

    <xsl:variable name="vrtfResult"> 
    <xsl:apply-templates select="$pCurrent" mode="explode"/> 
    </xsl:variable> 
    <xsl:copy-of select="$vrtfResult"/> 

    <xsl:variable name="vResult" select="ext:node-set($vrtfResult)/*"/> 

    <xsl:if test="contains(substring-after($vResult/csvGuestString, $vResult/guestId), ',')"> 
    <xsl:call-template name="explode"> 
     <xsl:with-param name="pCurrent" select="$vResult"/> 
     <xsl:with-param name="pLastId" select="$vResult/guestId"/> 
    </xsl:call-template> 
    </xsl:if> 
</xsl:template> 

<xsl:template match="node()" mode="explode"> 
    <xsl:call-template name="identity"/> 
</xsl:template> 

<xsl:template match="reservation" mode="explode"> 
    <xsl:copy> 
    <xsl:apply-templates select="node()" mode="explode"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="guestId[contains(.,',')]" mode="explode"> 
    <csvGuestString><xsl:value-of select="."/></csvGuestString> 
    <guestId><xsl:value-of select="substring-before(., ',')"/></guestId> 
</xsl:template> 

<xsl:template match="guestId" mode="explode"> 
    <guestId> 
    <xsl:value-of select="substring-before(substring-after(concat(../csvGuestString, ','), concat(current(),',')), ',')"/> 
    </guestId> 
</xsl:template> 
</xsl:stylesheet> 

当这种变换所提供的XML文档应用:

<reservations> 
    <reservation> 
    <id>1</id> 
    <guestId>1111</guestId> 
    <!-- other fields --> 
    </reservation> 
    <reservation> 
    <id>2</id> 
    <guestId>2222,3333,4444</guestId> 
    <!-- other fields --> 
    </reservation> 
</reservations> 

想要的,正确的结果产生:

<reservations> 
    <reservation> 
    <id>1</id> 
    <guestId>1111</guestId> 
    <csvGuestString>1111</csvGuestString> 
    <!-- other fields --> 
    </reservation> 
    <reservation> 
    <id>2</id> 
    <csvGuestString>2222,3333,4444</csvGuestString> 
    <guestId>2222</guestId> 
    <!-- other fields --> 
    </reservation> 
    <reservation> 
    <id>2</id> 
    <csvGuestString>2222,3333,4444</csvGuestString> 
    <guestId>3333</guestId> 
    <!-- other fields --> 
    </reservation> 
    <reservation> 
    <id>2</id> 
    <csvGuestString>2222,3333,4444</csvGuestString> 
    <guestId>4444</guestId> 
    <!-- other fields --> 
    </reservation> 
</reservations> 
+0

我喜欢它。谢谢@Dimitre。 – ABach 2012-08-09 00:18:23

+0

@ABach:不客气。 – 2012-08-09 01:18:16

1

使用的EXSLT字符串函数的tokenize方法:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:str="http://exslt.org/strings" 
    exclude-result-prefixes="str"> 

<xsl:strip-space elements="*"/> 
<xsl:output indent="yes"/> 

<xsl:template match="@* | node()"> 
    <xsl:copy> 
    <xsl:apply-templates select="@* | node()"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="reservation"> 
    <xsl:variable name="this" select="."/> 
    <xsl:for-each select="str:tokenize(guestId, ',')" > 
    <xsl:apply-templates select="$this" mode="copy"> 
     <xsl:with-param name="id" select="."/> 
    </xsl:apply-templates> 
    </xsl:for-each> 
</xsl:template> 

<xsl:template match="reservation" mode="copy"> 
    <xsl:param name="id"/> 
    <xsl:copy> 
    <xsl:apply-templates select="@*"/> 
    <xsl:apply-templates select="node()"> 
     <xsl:with-param name="id" select="$id"/> 
    </xsl:apply-templates> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="reservation/guestId"> 
    <xsl:param name="id"/> 
    <csvGuestString> 
    <xsl:value-of select="."/> 
    </csvGuestString> 
    <guestId> 
    <xsl:value-of select="$id"/> 
    </guestId> 
</xsl:template> 

</xsl:stylesheet> 

这样xsltproc的将您输入样品放入

<reservations> 
    <reservation> 
    <id>1</id> 
    <csvGuestString>1111</csvGuestString> 
    <guestId>1111</guestId> 
    <!-- other fields --> 
    </reservation> 
    <reservation> 
    <id>2</id> 
    <csvGuestString>2222,3333,4444</csvGuestString> 
    <guestId>2222</guestId> 
    <!-- other fields --> 
    </reservation> 
    <reservation> 
    <id>2</id> 
    <csvGuestString>2222,3333,4444</csvGuestString> 
    <guestId>3333</guestId> 
    <!-- other fields --> 
    </reservation> 
    <reservation> 
    <id>2</id> 
    <csvGuestString>2222,3333,4444</csvGuestString> 
    <guestId>4444</guestId> 
    <!-- other fields --> 
    </reservation> 
</reservations> 
+0

有意义:如果您已经在使用EXSLT,为什么不利用'tokenize'?感谢您的帮助,@Martin。 – ABach 2012-08-09 00:19:01