对元素和属性名称的动态分组 [英] Dynamic grouping on element and attributes names

查看:69
本文介绍了对元素和属性名称的动态分组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想将XPath的结果按元素名称(然后按相同的属性名称)归类.注意:XML数据可能不一致,并且某些具有相同名称的元素可能具有不同的属性,因此它们需要不同的标题.

我似乎无法用语言写出我的问题,所以最好使用一个例子.

XML:

<pets>  
    <dog name="Frank" cute="yes" color"brown" type="Lab"/>
    <cat name="Fluffy" cute="yes" color="brown"/>
    <cat name="Lucy" cute="no" color="brown"/>
    <dog name="Spot" cute="no" color="brown"/>
    <dog name="Rover" cute="yes" color="brown"/>
    <dog name="Rupert" cute="yes" color="beige" type="Pug"/>
    <cat name="Simba" cute="yes" color="grey"/>
    <cat name="Princess" color="brown"/>

</pets>

XPath:

//*[@color='brown']

输出应具有什么样的外观(不同元素的标题不同):

ElementName  Color   Cute     Name     Type   
Dog          Brown   Yes      Frank    Lab


ElementName  Color   Cute     Name       
Dog          Brown   No       Spot    
Dog          Brown   Yes      Rover


ElementName  Color   Cute     Name     
Cat          Brown   Yes      Fluffy    
Cat          Brown   No       Lucy



ElementName  Color   Name     
Cat          Brown   Princess  

我当前拥有的XSL(简体!):

<xsl:apply-templates select="//*[@color='brown']" mode="result">
    <xsl:sort select="name()" order="ascending"/>
</xsl:apply-templates>


<xsl:template match="@*|node()" mode="result">
    <tr>
        <th align="left">Element</th>

        <xsl:for-each select="@*">
            <xsl:sort select="name()" order="ascending"/>
            <th align="left">
                <xsl:value-of select="name()"/>
            </th>
        </xsl:for-each>
    </tr>

    <tr>
        <td align="left">
            <xsl:value-of select="name()"/>
         </td>
         <xsl:for-each select="@*">
             <xsl:sort select="name()" order="ascending"/>
             <td align="left">
                 <xsl:value-of select="."/>
             </td>
         </xsl:for-each>
     </tr>
</xsl:template>

以上XSL都按照我想要的方式对它们进行了正确排序..但是,现在我需要进行某种检查,以查看哪些元素具有相同的名称,然后,如果它们具有相同的名称,它们是否具有相同的属性.完成此检查后,我可以将常规标题"放在具有匹配元素名称和属性的记录集的上方.

我认为我可以使用xsl:choose xsl:when并进行一些测试.我在想(正确的订购完成后):

If element name != previous element name
    create headings
Else if all attributes != all previous element's attributes
    create headings

我想我最大的问题是,我不知道如何检查以前返回的数据集是什么...有人可以告诉我该怎么做吗?

或者如果我要解决这个错误..请问我有一个更好的解决方案吗?

希望一切都有道理!让我知道您是否需要澄清!

在此先感谢您的耐心等待和回应! :)

解决方案

此转换不对具有相同数量属性的集合进行任何假设-完全没有假设.

<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:key name="kAnimalByProperties" match="animal"
  use="concat(@atype, .)"/>

 <xsl:variable name="vrtfNewDoc">
  <xsl:apply-templates select="/pets/*">
    <xsl:sort select="name()"/>
  </xsl:apply-templates>
 </xsl:variable>

 <xsl:template match="pets/*">
   <animal atype="{name()}">
     <xsl:copy-of select="@*"/>
     <xsl:for-each select="@*">
       <xsl:sort select="name()"/>
         <attrib>|<xsl:value-of select="name()"/>|</attrib>
     </xsl:for-each>
   </animal>
 </xsl:template>

 <xsl:template match="/">
   <xsl:for-each select="ext:node-set($vrtfNewDoc)">
     <xsl:for-each select=
     "*[generate-id()
       =generate-id(key('kAnimalByProperties',
                        concat(@atype, .)
                        )[1]
                    )
       ]">
        <table border="1">
          <tr>
            <td>Element Name</td>
            <xsl:for-each select="*">
              <td><xsl:value-of select="translate(.,'|','')"/></td>
            </xsl:for-each>
          </tr>
          <xsl:for-each select=
          "key('kAnimalByProperties', concat(@atype, .))">
            <xsl:variable name="vcurAnimal" select="."/>
            <tr>
              <td><xsl:value-of select="@atype"/></td>
              <xsl:for-each select="*">
                <td>
                  <xsl:value-of select=
                   "$vcurAnimal/@*[name()=translate(current(),'|','')]"/>
                </td>
              </xsl:for-each>
            </tr>
          </xsl:for-each>
        </table>
        <p/>
     </xsl:for-each>
   </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

应用于提供的XML文档:

<pets>
    <dog name="Frank" cute="yes" color="brown" type="Lab"/>
    <cat name="Fluffy" cute="yes" color="brown"/>
    <cat name="Lucy" cute="no" color="brown"/>
    <dog name="Spot" cute="no" color="brown"/>
    <dog name="Rover" cute="yes" color="brown"/>
    <dog name="Rupert" cute="yes" color="beige" type="Pug"/>
    <cat name="Simba" cute="yes" color="grey"/>
    <cat name="Princess" color="brown"/>
</pets>

产生了所需的正确结果:

<table border="1">
   <tr>
      <td>Element Name</td>
      <td>color</td>
      <td>cute</td>
      <td>name</td>
   </tr>
   <tr>
      <td>cat</td>
      <td>brown</td>
      <td>yes</td>
      <td>Fluffy</td>
   </tr>
   <tr>
      <td>cat</td>
      <td>brown</td>
      <td>no</td>
      <td>Lucy</td>
   </tr>
   <tr>
      <td>cat</td>
      <td>grey</td>
      <td>yes</td>
      <td>Simba</td>
   </tr>
</table>
<p/>
<table border="1">
   <tr>
      <td>Element Name</td>
      <td>color</td>
      <td>name</td>
   </tr>
   <tr>
      <td>cat</td>
      <td>brown</td>
      <td>Princess</td>
   </tr>
</table>
<p/>
<table border="1">
   <tr>
      <td>Element Name</td>
      <td>color</td>
      <td>cute</td>
      <td>name</td>
      <td>type</td>
   </tr>
   <tr>
      <td>dog</td>
      <td>brown</td>
      <td>yes</td>
      <td>Frank</td>
      <td>Lab</td>
   </tr>
   <tr>
      <td>dog</td>
      <td>beige</td>
      <td>yes</td>
      <td>Rupert</td>
      <td>Pug</td>
   </tr>
</table>
<p/>
<table border="1">
   <tr>
      <td>Element Name</td>
      <td>color</td>
      <td>cute</td>
      <td>name</td>
   </tr>
   <tr>
      <td>dog</td>
      <td>brown</td>
      <td>no</td>
      <td>Spot</td>
   </tr>
   <tr>
      <td>dog</td>
      <td>brown</td>
      <td>yes</td>
      <td>Rover</td>
   </tr>
</table>
<p/>

I would like to categorize results from an XPath under headings by element name (and then by same attribute names). Note: XML data could be inconsistent and some elements with the same name could have different attributes, therefore they need different headings.

I can't seem to write out my problem in words, so it might be best to use an example..

XML:

<pets>  
    <dog name="Frank" cute="yes" color"brown" type="Lab"/>
    <cat name="Fluffy" cute="yes" color="brown"/>
    <cat name="Lucy" cute="no" color="brown"/>
    <dog name="Spot" cute="no" color="brown"/>
    <dog name="Rover" cute="yes" color="brown"/>
    <dog name="Rupert" cute="yes" color="beige" type="Pug"/>
    <cat name="Simba" cute="yes" color="grey"/>
    <cat name="Princess" color="brown"/>

</pets>

XPath:

//*[@color='brown']

What the output should sort of look like (with the different headings for different elements):

ElementName  Color   Cute     Name     Type   
Dog          Brown   Yes      Frank    Lab


ElementName  Color   Cute     Name       
Dog          Brown   No       Spot    
Dog          Brown   Yes      Rover


ElementName  Color   Cute     Name     
Cat          Brown   Yes      Fluffy    
Cat          Brown   No       Lucy



ElementName  Color   Name     
Cat          Brown   Princess  

The XSL I currently have (simplified!):

<xsl:apply-templates select="//*[@color='brown']" mode="result">
    <xsl:sort select="name()" order="ascending"/>
</xsl:apply-templates>


<xsl:template match="@*|node()" mode="result">
    <tr>
        <th align="left">Element</th>

        <xsl:for-each select="@*">
            <xsl:sort select="name()" order="ascending"/>
            <th align="left">
                <xsl:value-of select="name()"/>
            </th>
        </xsl:for-each>
    </tr>

    <tr>
        <td align="left">
            <xsl:value-of select="name()"/>
         </td>
         <xsl:for-each select="@*">
             <xsl:sort select="name()" order="ascending"/>
             <td align="left">
                 <xsl:value-of select="."/>
             </td>
         </xsl:for-each>
     </tr>
</xsl:template>

This above XSL sorts them correctly in the way I want.. but now I need some sort of check to see which elements have the same name, and then if they have the same name, do they have the same attributes. Once I complete this check, I can then put general "Headings" above sets of records with matching element name and attributes.

I figured I could use xsl:choose xsl:when and do some tests. I was thinking (after the correct ordering has been done):

If element name != previous element name
    create headings
Else if all attributes != all previous element's attributes
    create headings

I guess my biggest problem is, is that I don't know how to check what the previous returned data set was... Can someone please tell me how to do this?

Or if I am approaching this wrong.. lead me to a better solution?

Hope that all made sense! Let me know if you need clarification!

Thanks in advance for your patience and responses! :)

解决方案

This transformation doesn't make any assumptions about the sets having the same number of attributes -- no assumptions at all.

<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:key name="kAnimalByProperties" match="animal"
  use="concat(@atype, .)"/>

 <xsl:variable name="vrtfNewDoc">
  <xsl:apply-templates select="/pets/*">
    <xsl:sort select="name()"/>
  </xsl:apply-templates>
 </xsl:variable>

 <xsl:template match="pets/*">
   <animal atype="{name()}">
     <xsl:copy-of select="@*"/>
     <xsl:for-each select="@*">
       <xsl:sort select="name()"/>
         <attrib>|<xsl:value-of select="name()"/>|</attrib>
     </xsl:for-each>
   </animal>
 </xsl:template>

 <xsl:template match="/">
   <xsl:for-each select="ext:node-set($vrtfNewDoc)">
     <xsl:for-each select=
     "*[generate-id()
       =generate-id(key('kAnimalByProperties',
                        concat(@atype, .)
                        )[1]
                    )
       ]">
        <table border="1">
          <tr>
            <td>Element Name</td>
            <xsl:for-each select="*">
              <td><xsl:value-of select="translate(.,'|','')"/></td>
            </xsl:for-each>
          </tr>
          <xsl:for-each select=
          "key('kAnimalByProperties', concat(@atype, .))">
            <xsl:variable name="vcurAnimal" select="."/>
            <tr>
              <td><xsl:value-of select="@atype"/></td>
              <xsl:for-each select="*">
                <td>
                  <xsl:value-of select=
                   "$vcurAnimal/@*[name()=translate(current(),'|','')]"/>
                </td>
              </xsl:for-each>
            </tr>
          </xsl:for-each>
        </table>
        <p/>
     </xsl:for-each>
   </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

When applied on the provided XML document:

<pets>
    <dog name="Frank" cute="yes" color="brown" type="Lab"/>
    <cat name="Fluffy" cute="yes" color="brown"/>
    <cat name="Lucy" cute="no" color="brown"/>
    <dog name="Spot" cute="no" color="brown"/>
    <dog name="Rover" cute="yes" color="brown"/>
    <dog name="Rupert" cute="yes" color="beige" type="Pug"/>
    <cat name="Simba" cute="yes" color="grey"/>
    <cat name="Princess" color="brown"/>
</pets>

the wanted, correct result is produced:

<table border="1">
   <tr>
      <td>Element Name</td>
      <td>color</td>
      <td>cute</td>
      <td>name</td>
   </tr>
   <tr>
      <td>cat</td>
      <td>brown</td>
      <td>yes</td>
      <td>Fluffy</td>
   </tr>
   <tr>
      <td>cat</td>
      <td>brown</td>
      <td>no</td>
      <td>Lucy</td>
   </tr>
   <tr>
      <td>cat</td>
      <td>grey</td>
      <td>yes</td>
      <td>Simba</td>
   </tr>
</table>
<p/>
<table border="1">
   <tr>
      <td>Element Name</td>
      <td>color</td>
      <td>name</td>
   </tr>
   <tr>
      <td>cat</td>
      <td>brown</td>
      <td>Princess</td>
   </tr>
</table>
<p/>
<table border="1">
   <tr>
      <td>Element Name</td>
      <td>color</td>
      <td>cute</td>
      <td>name</td>
      <td>type</td>
   </tr>
   <tr>
      <td>dog</td>
      <td>brown</td>
      <td>yes</td>
      <td>Frank</td>
      <td>Lab</td>
   </tr>
   <tr>
      <td>dog</td>
      <td>beige</td>
      <td>yes</td>
      <td>Rupert</td>
      <td>Pug</td>
   </tr>
</table>
<p/>
<table border="1">
   <tr>
      <td>Element Name</td>
      <td>color</td>
      <td>cute</td>
      <td>name</td>
   </tr>
   <tr>
      <td>dog</td>
      <td>brown</td>
      <td>no</td>
      <td>Spot</td>
   </tr>
   <tr>
      <td>dog</td>
      <td>brown</td>
      <td>yes</td>
      <td>Rover</td>
   </tr>
</table>
<p/>

这篇关于对元素和属性名称的动态分组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆