簡體   English   中英

XSL按元素分組xml

[英]XSL grouping xml by element

我正在使用XSLT1.0(處理器無法處理2.0),嘗試對xml結構的輸出進行分組時遇到問題:

<行>
<訂單>
<文本>一些命令文本1
</ text>
</ order>
</ row>

<行>
<付款>
<文本>一些付款文本1
</ text>
</ payment>
</ row>

<行>
<訂單>
<文本>一些命令文本2
</ text>
</ order>
</ row>

<行>
<聯系人>
<text>一些聯系方式1
</ text>
</ contact>
</ row>

<行>
<聯系人>
<text>一些聯系方式2
</ text>
</ contact>
</ row>

今天,我們選擇所有行並為每個行調用Apply模板(每種類型都有自己的模板,該模板寫出其主體),從而創建如下輸出:

訂單:一些訂單文字1
訂單:一些訂單文字2
付款方式:部分付款文字1
聯系人:一些聯系方式1
聯系人:一些聯系方式2

但是我想(在XSLT 1.0中)對輸出進行分組,以便:

訂購

  1. 一些訂單文字1
  2. 一些命令文本2

付款

  1. 一些付款文字1

聯系

  1. 一些聯系方式1
  2. 一些聯系方式2

顯然,這里除了訂購,付款和聯系外還有許多其他元素類型,因此按顯式元素名稱進行選擇不是解決方案。

編輯

是的,一些很好的答案,如果我有一個說的結構,Muenchian分組解將如何改變

<customers>
  <person>
    <row>....</row> (row is same as above)
    <row>....</row>
  </person>
  <person>
    <row>....</row>
    <row>....</row>
    <row>....</row>
  </person>

然后的關鍵:

  <xsl:key name="type" match="row/*" use="local-name()"/>

將跨所有人選擇所有行,這不是我想要的。 也感謝您的好評。

在XSLT 1.0中執行此操作,您需要使用Muenchian分組 ,但是(在我看來)使用XSLT 2.0中的xsl:for-each-group更容易解決。

以下XSLT 1.0樣式表將滿足您的要求,關鍵是使用一個鍵(doh!),該鍵將允許您對節點的本地名稱進行分組。

輸入:

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <row>
    <order>
      <text>some order text 1</text>
    </order>
  </row>

  <row>
    <payment>
      <text>some payment text 1</text>
    </payment>
  </row>

  <row>
    <order>
      <text>some order text 2</text>
    </order>
  </row>

  <row>
    <contact>
      <text>some contact details 1</text>
    </contact>
  </row>

  <row>
    <contact>
      <text>some contact details 2</text>
    </contact>
  </row>
</root>

XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="text"/>
  <xsl:key name="type" match="row/*" use="local-name()"/>

  <xsl:template match="root">
    <xsl:for-each select="row/*[
      generate-id() = generate-id(key('type', local-name())[1])]">
      <xsl:value-of select="local-name()"/>
      <xsl:text>&#x0a;</xsl:text>
      <xsl:for-each select="key('type', local-name())">
        <xsl:value-of select="concat('  ', position(), '. ')"/>
        <xsl:apply-templates select="text"/>
        <xsl:text>&#x0a;</xsl:text>
      </xsl:for-each>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

輸出:

order
  1. some order text 1
  2. some order text 2
payment
  1. some payment text 1
contact
  1. some contact details 1
  2. some contact details 2

嘗試:

<xsl:template match="(parent element-whatever contains the 'row' elements)">
  <xsl:apply-templates>
    <xsl:sort select="name(*)" />
  </xsl:apply-templates>
</xsl:template>

這將按第一個孩子的名字對行元素進行排序。

該模板添加了一個標題:

<xsl:template match="row">
    <xsl:copy>
    <xsl:if test="not(preceding-sibling::*[name(*) = name(current()/*)])">
      <!-- Output header here -->
      <xsl:value-of select="name(*)" />
    </xsl:if>
    <xsl:apply-templates select="@* | node()"/>
  </xsl:copy>
</xsl:template>

測試基本上說:“如果以前沒有同名兄弟姐妹,則輸出此信息”。

以@Flynn的答案為基礎...

如果您有父級模板(樣本中未顯示):

<xsl:template match="row-parent">
  <xsl:apply-templates select="row">
    <xsl:sort select="name(*[1])" />
  </xsl:apply-templates>
</xsl:template>

請注意,通過選擇“行”而不是默認值(所有子級,包括文本節點),我們避免選擇包含空格且對於我們的輸出不理想的文本節點。

然后,為了添加節標題,用於處理子項的模板使用一個條件來查看這是否是其節的第一行:

<xsl:template match="row">
   <xsl:variable name="childName" select="name(*[1])"/>
   <!-- if this is the first row with an element child of this name -->
   <xsl:if test="not(preceding-sibling::row[name(*[1]) = $childName])">
      <xsl:value-of select="concat('&#10;',
         translate(substring($childName, 1, 1), $lower, $upper),
         substring($childName, 2), '&#10;&#10;')"/>
   </xsl:if>

然后以所需的格式輸出該組每一行的數據:

   <xsl:number level="any" count="row[name(*[1]) = $childName]" format=" 1. "
      from="row-parent"/>
   <xsl:value-of select="normalize-space(*[1])"/>
   <xsl:text>&#10;</xsl:text>
</xsl:template>

像往常一樣,$ lower和$ upper在模板(或樣式表)的頂部定義為

<xsl:variable name="lower" select="'abcdefghijklmnopqrstuvwxyz'"/>
<xsl:variable name="upper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>

並使樣式表使用“文本”輸出方法:

<xsl:output method="text"/>

輸入上方的樣式表的輸出(在<row-parent>包裝器內)為:

Contact

 1. some contact details 1
 2. some contact details 2

Order

 1. some order text 1
 2. some order text 2

Payment

 1. some payment text 1

另外,更魯棒地,您可以使用Muenchian分組 :首先按子元素名稱對行進行分組 ,然后對(輸出每個組的標頭並)處理組中的所有行。

除了使用分組方法的良好答案外,此樣式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"/>
    <xsl:variable name="vSort" select="'|order|payment|contact|'"/>
    <xsl:template match="/">
        <xsl:apply-templates select="*/row">
            <xsl:sort select="string-length(
                                 substring-before($vSort,
                                                  concat('|',
                                                         name(),
                                                         '|')))"/>
        </xsl:apply-templates>
    </xsl:template>
    <xsl:template match="row/*">
        <xsl:variable name="vName" select="name()"/>
        <xsl:variable name="vNumber">
            <xsl:number level="any" count="*[name()=$vName]" from="/"/>
        </xsl:variable>
        <xsl:if test="$vNumber = 1">
            <xsl:value-of select="concat(translate(substring(name(),1,1),
                                                       'opc',
                                                       'OPC'),
                                             substring(name(),2),
                                             '&#xA;')"/>
        </xsl:if>
        <xsl:value-of select="concat($vNumber,'. ',text,'&#xA;')"/>
    </xsl:template>
</xsl:stylesheet>

輸出(輸入格式正確):

Order
1.  some order text 1
Payment
1.  some payment text 1
2.  some order text 2
Contact
1.  some contact details 1
2.  some contact details 2

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM