使用 XSLT muenchian-grouping 进行嵌套分组

Posted

技术标签:

【中文标题】使用 XSLT muenchian-grouping 进行嵌套分组【英文标题】:Nested grouping using XSLT muenchian-grouping 【发布时间】:2015-04-10 06:09:43 【问题描述】:

我必须使用 Oracle Service Bus 在 xslt 1.0 中对 xml 文档进行分组。

这是示例输入文件(简体):

    <?xml version="1.0" encoding="UTF-8"?>
    <EMailData>
       <property name="A">
          <property name="B">
               <property name="C">
                <row>
                   <property name="C1">
                      <value>ValC1</value>
                   </property>
                   <property name="C2">
                      <value>ValC2</value>
                   </property>
                   <property name="C3">
                      <value>Valc3</value>
                   </property>
                   <property name="C4">
                      <value>Valc4</value>
                   </property>
                 </row>  
             </property> 
             <property name="C">
                <row>
                   <property name="C1">
                      <value>ValC1</value>
                   </property>
                   <property name="C2">
                      <value>ValC2</value>
                   </property>
                   <property name="C3">
                      <value>Valc3</value>
                   </property>
                   <property name="C4">
                      <value>Valc4</value>
                   </property>
                 </row>  
             </property> 
             <property name="D">
                <row>
                   <property name="D1">
                      <value>ValD1</value>
                   </property>
                   <property name="D2">
                      <value>VALd2</value>
                   </property>
                   <property name="D3-InnerElement"> //Need to Group this too
                      <row>
                         <property name="Status">
                            <value>Status122</value>
                         </property>
                      </row>
                   </property>
                   <property name="D3-InnerElement">
                      <row>
                         <property name="Status">
                            <value>Status123</value>
                         </property>
                      </row>
                   </property>
                   <property name="D3-InnerElement">
                      <row>
                         <property name="Status">
                            <value>Status124</value>
                         </property>
                      </row>
                   </property>
                </row>
            </property>
             <property name="D">
                <row>
                   <property name="D1">
                      <value>ValD1</value>
                   </property>
                   <property name="D2">
                      <value>VALd2</value>
                   </property>
                   <property name="D3-InnerElement">
                      <row>
                         <property name="Status">
                            <value>Status122</value>
                         </property>
                      </row>
                   </property>
                   <property name="D3-InnerElement">
                      <row>
                         <property name="Status">
                            <value>Status123</value>
                         </property>
                      </row>
                   </property>
                   <property name="D3-InnerElement">
                      <row>
                         <property name="Status">
                            <value>Status124</value>
                         </property>
                      </row>
                   </property>
                </row>
            </property>             
          </property>
       </property>
    </EMailData>

我的 XSLT 逻辑:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

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

<xsl:key name="group" match="/*/*/*/property" use="@name"/>

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

<xsl:template match="/*/*/*[property[@name]]">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:for-each select="*[generate-id() = generate-id(key('group', @name)[1])]">
  <xsl:copy>
    <xsl:copy-of select="@*"/>
    <xsl:apply-templates select="key('group', @name)/*"/>
  </xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>

<!--Change for Inner Hierarchy-->

<xsl:key name="inner-group" match="/*/*/*/*/property" use="@name"/>

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

<xsl:template match="/*/*/*/*[property[@name]]">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:for-each select="*[generate-id() = generate-id(key('inner-group', @name)[1])]">
  <xsl:copy>
    <xsl:copy-of select="@*"/>
    <xsl:apply-templates select="key('inner-group', @name)/*"/>
  </xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

预期 o/p

<?xml version="1.0" encoding="UTF-8"?>
<EMailData>
   <property name="A">
      <property name="B">
         <property name="C">
            <row>
               <property name="C1">
                  <value>ValC1</value>
               </property>
               <property name="C2">
                  <value>ValC2</value>
               </property>
               <property name="C3">
                  <value>Valc3</value>
               </property>
               <property name="C4">
                  <value>Valc4</value>
               </property>
            </row>
            <row>
               <property name="C1">
                  <value>ValC1</value>
               </property>
               <property name="C2">
                  <value>ValC2</value>
               </property>
               <property name="C3">
                  <value>Valc3</value>
               </property>
               <property name="C4">
                  <value>Valc4</value>
               </property>
            </row>
         </property>
         <property name="D">
            <row>
               <property name="D1">
                  <value>ValD1</value>
               </property>
               <property name="D2">
                  <value>VALd2</value>
               </property>
               <property name="D3-InnerElement"> //Need to Group this too
                      <row>
                     <property name="Status">
                        <value>Status122</value>
                     </property>
                  </row>
               </property>
               <property name="D3-InnerElement">
                  <row>
                     <property name="Status">
                        <value>Status123</value>
                     </property>
                  </row>
                  <row>
                     <property name="Status">
                        <value>Status124</value>
                     </property>
                  </row>
               </property>
            </row>
            <row>
               <property name="D1">
                  <value>ValD1</value>
               </property>
               <property name="D2">
                  <value>VALd2</value>
               </property>
               <property name="D3-InnerElement">
                  <row>
                     <property name="Status">
                        <value>Status122</value>
                     </property>
                  </row>
                  <row>
                     <property name="Status">
                        <value>Status123</value>
                     </property>
                  </row>
                  <row>
                     <property name="Status">
                        <value>Status124</value>
                     </property>
                  </row>
               </property>
            </row>
         </property>
      </property>
   </property>
</EMailData>

但是 D3-innerelement 没有分组。告诉我哪里出错了!!

o/p 对于我的 XSLT

    <?xml version="1.0" encoding="UTF-8"?>
<EMailData>
   <property name="A">
      <property name="B">
         <property name="C">
            <row>
               <property name="C1">
                  <value>ValC1</value>
               </property>
               <property name="C2">
                  <value>ValC2</value>
               </property>
               <property name="C3">
                  <value>Valc3</value>
               </property>
               <property name="C4">
                  <value>Valc4</value>
               </property>
            </row>
            <row>
               <property name="C1">
                  <value>ValC1</value>
               </property>
               <property name="C2">
                  <value>ValC2</value>
               </property>
               <property name="C3">
                  <value>Valc3</value>
               </property>
               <property name="C4">
                  <value>Valc4</value>
               </property>
            </row>
         </property>
         <property name="D">
            <row>
               <property name="D1">
                  <value>ValD1</value>
               </property>
               <property name="D2">
                  <value>VALd2</value>
               </property>
               <property name="D3-InnerElement"> //Need to Group this too
                          <row>
                     <property name="Status">
                        <value>Status122</value>
                     </property>
                  </row>
               </property>
               <property name="D3-InnerElement">
                  <row>
                     <property name="Status">
                        <value>Status123</value>
                     </property>
                  </row>
               </property>
               <property name="D3-InnerElement">
                  <row>
                     <property name="Status">
                        <value>Status124</value>
                     </property>
                  </row>
               </property>
            </row>
            <row>
               <property name="D1">
                  <value>ValD1</value>
               </property>
               <property name="D2">
                  <value>VALd2</value>
               </property>
               <property name="D3-InnerElement">
                  <row>
                     <property name="Status">
                        <value>Status122</value>
                     </property>
                  </row>
               </property>
               <property name="D3-InnerElement">
                  <row>
                     <property name="Status">
                        <value>Status123</value>
                     </property>
                  </row>
               </property>
               <property name="D3-InnerElement">
                  <row>
                     <property name="Status">
                        <value>Status124</value>
                     </property>
                  </row>
               </property>
            </row>
         </property>
      </property>
   </property>
</EMailData>

提前致谢!

【问题讨论】:

除了发布你的预期输出,你能用文字解释你想要做什么吗?理解样式表需要一些时间。 我只想将特定元素内的所有单独的行分组到一个元素本身之下。如中,不重复元素 C。只需按行分隔它们。这必须进一步深入到内部的元素。这就是问题发生的地方 解释不够。您能否尝试详细说明。另外,我看不到您的输入和预期输出是同步的,例如,我可以在预期输出中看到 Status133 作为文本节点,但在输入中看不到。你能不能也解决这个问题。 我已经编辑了代码。我现在清楚了吗?我只需要对元素 C 和 D 进行分组。在它们下面有多行。我可以使用 XSLT 逻辑来做到这一点。但是元素 D 有另一个元素(D3-InnerElement),它必须以相同的方式分组。我的代码没有解决这个问题。请纠正我。 HeisenBerg,这个问题有什么联系:***.com/questions/28429646/…? 【参考方案1】:

下面是使用 Muenchian 分组的解决方案:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:output method="xml" indent="yes"/>

<xsl:key name="group" match="property" use="@name"/>

<xsl:template match="/EMailData/property/property | /EMailData/property/property/property/row">
    <xsl:variable name="id" select="generate-id()"/>
    <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:for-each select="property[count(. | key('group', @name)[$id = generate-id(parent::*)][1]) = 1]">
            <xsl:copy>
                <xsl:copy-of select="@*"/>
                <xsl:apply-templates select="key('group', @name)[$id = generate-id(parent::*)]/*"/>
            </xsl:copy>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>
<xsl:template match="@* | node()">
    <xsl:copy>
        <xsl:apply-templates select="@* | node()" />
    </xsl:copy>
</xsl:template>
</xsl:stylesheet>

【讨论】:

【参考方案2】:

此解决方案不是基于 Muenchian 的分组,但认为它会有所帮助:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="*[property]">
    <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:for-each select="property[not(@name = preceding-sibling::property/@name)]">
            <xsl:copy>
                <xsl:copy-of select="@*"/>
                <xsl:apply-templates select="../property[@name = current()/@name]/*"/>
            </xsl:copy>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>
<xsl:template match="@* | node()">
    <xsl:copy>
        <xsl:apply-templates select="@* | node()" />
    </xsl:copy>
</xsl:template>
</xsl:stylesheet>

这里,第二个模板是恒等转换模板,用于复制所有属性和节点。

第一个模板匹配具有至少一个property 子元素的元素,或者简单地说,“要按@name 分组的property 元素的父元素”。 您也可以将模板匹配更改为:

<xsl:template match="/EMailData/property/property | /EMailData/property/property/property/row">

for-each 位于第一个 property 上,在当前父级中具有特定的 @name 值(请参阅使用 preceding-sibling 的条件)。 并且对于每次迭代,模板应用于property 元素的所有子元素,当前(每个元素的)@name,即,将单个父元素的property 元素按其@name 的值分组.

内部property 元素调用相同的模板,甚至按@name 分组。

【讨论】:

Lingamurthy 干得好。谢谢。它工作正常。你能简单解释一下吗? 虽然我无法找到使用 Muenchian 分组的方法,但很高兴它对您有所帮助。让我用一些解释来编辑我的答案。 @HeisenBerg 请使用 Muenchian 的分组查看我的第二个答案。 两种解决方案都可以正常工作。但我对第一个解决方案的易用性感到惊讶。踢自己因为没有在第一时间得到它。我觉得第一个解决方案效率更高。 ? 不,@HeisenBerg,第一个解决方案的效率远低于使用 Muenchian 的分组。密钥可以更快地访问节点。您可以尝试使用更大的数据并运行一些案例来感受差异。

以上是关于使用 XSLT muenchian-grouping 进行嵌套分组的主要内容,如果未能解决你的问题,请参考以下文章

XSLT:如何使用 XSLT 1.0 和 XALAN 处理器转换部分转义的 XML?

将命名空间从 java 传递给 xslt,并使用 java 中的参数作为 xslt 中的节点

XSLT 使用 xslt 2.0 或更高版本将纯文本文件处理为 XML

使用XSLT从文本文件中删除第一行

如何使用 xslt 更改单个标签?

使用 Xslt 将 XML 转换为 XML