2012-08-08 76 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