Динамическая группировка по именам элементов и атрибутов - PullRequest
1 голос
/ 23 июля 2010

Я хотел бы классифицировать результаты 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

Думаю, моя самая большая проблема заключается в том, что я не знаю, как проверить, какой предыдущий набор данных был ... Можеткто-нибудь, пожалуйста, скажите мне, как это сделать?

Или, если я подхожу к этому неправильно ... привести меня к лучшему решению?

Надеюсь, что все это имеет смысл!Дайте мне знать, если вам нужны разъяснения!

Заранее спасибо за ваше терпение и ответы!:)

Ответы [ 2 ]

1 голос
/ 23 июля 2010

Это преобразование не делает никаких предположений о наборах, имеющих одинаковое количество атрибутов - вообще никаких предположений .

<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/>
0 голосов
/ 23 июля 2010

Эта таблица стилей:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="ByName-AttNum" match="*/*[@color='brown']" use="concat(name(),'++',count(@*))"/>
    <xsl:template match="/">
        <html>
            <xsl:apply-templates/>
        </html>
    </xsl:template>
    <xsl:template match="*/*[generate-id(.) = generate-id(key('ByName-AttNum',concat(name(),'++',count(@*)))[1])]">
        <table>
            <tr>
                <th>ElementName</th>
                <xsl:apply-templates select="@*" mode="headers">
                    <xsl:sort select="name()"/>
                </xsl:apply-templates>
            </tr>
            <xsl:apply-templates select="key('ByName-AttNum',concat(name(),'++',count(@*)))" mode="list"/>
        </table>
    </xsl:template>
    <xsl:template match="*" mode="list">
        <tr>
            <td>
                <xsl:value-of select="name()"/>
            </td>
            <xsl:apply-templates select="@*" mode="list">
                <xsl:sort select="name()"/>
            </xsl:apply-templates>
        </tr>
    </xsl:template>
    <xsl:template match="@*" mode="headers">
        <th>
            <xsl:value-of select="name()"/>
        </th>
    </xsl:template>
    <xsl:template match="@*" mode="list">
        <td>
            <xsl:value-of select="."/>
        </td>
    </xsl:template>
</xsl:stylesheet>

Результат:

<html>
    <table>
        <tr>
            <th>ElementName</th>
            <th>color</th>
            <th>cute</th>
            <th>name</th>
            <th>type</th>
        </tr>
        <tr>
            <td>dog</td>
            <td>brown</td>
            <td>yes</td>
            <td>Frank</td>
            <td>Lab</td>
        </tr>
    </table>
    <table>
        <tr>
            <th>ElementName</th>
            <th>color</th>
            <th>cute</th>
            <th>name</th>
        </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>
    </table>
    <table>
        <tr>
            <th>ElementName</th>
            <th>color</th>
            <th>cute</th>
            <th>name</th>
        </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>
    <table>
        <tr>
            <th>ElementName</th>
            <th>color</th>
            <th>name</th>
        </tr>
        <tr>
            <td>cat</td>
            <td>brown</td>
            <td>Princess</td>
        </tr>
    </table>
</html>

Примечание : предполагается, что все элементы, имеющие одинаковое количество атрибутов, имеют одинаковый атрибутname (как во входном примере).

EDIT : улучшенная разметка выходного сигнала.

EDIT 2 : другой вид решения: один заголовок свсе возможные атрибуты (например, шаблон CSV) и элементы заказа по количеству и имени атрибута.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="attrByName" match="pets/*/@*" use="name()"/>
    <xsl:variable name="attr" select="/pets/*/@*[count(.|key('attrByName',name())[1])=1]"/>
    <xsl:template match="pets">
        <html>
            <table>
                <tr>
                    <th>ElementName</th>
                    <xsl:apply-templates select="$attr" mode="headers">
                        <xsl:sort select="name()"/>
                    </xsl:apply-templates>
                </tr>
                <xsl:apply-templates select="*[@color='brown']">
                    <xsl:sort select="count(@*)" order="descending"/>
                    <xsl:sort select="name()"/>
                </xsl:apply-templates>
            </table>
        </html>
    </xsl:template>
    <xsl:template match="pets/*">
        <tr>
            <td>
                <xsl:value-of select="name()"/>
            </td>
            <xsl:apply-templates select="$attr" mode="list">
                <xsl:sort select="name()"/>
                <xsl:with-param name="node" select="."/>
            </xsl:apply-templates>
        </tr>
    </xsl:template>
    <xsl:template match="@*" mode="headers">
        <th>
            <xsl:value-of select="name()"/>
        </th>
    </xsl:template>
    <xsl:template match="@*" mode="list">
        <xsl:param name="node"/>
        <td>
            <xsl:value-of select="$node/@*[name()=name(current())]"/>
        </td>
    </xsl:template>
</xsl:stylesheet>

Результат:

<html>
    <table>
        <tr>
            <th>ElementName</th>
            <th>color</th>
            <th>cute</th>
            <th>name</th>
            <th>type</th>
        </tr>
        <tr>
            <td>dog</td>
            <td>brown</td>
            <td>yes</td>
            <td>Frank</td>
            <td>Lab</td>
        </tr>
        <tr>
            <td>cat</td>
            <td>brown</td>
            <td>yes</td>
            <td>Fluffy</td>
            <td></td>
        </tr>
        <tr>
            <td>cat</td>
            <td>brown</td>
            <td>no</td>
            <td>Lucy</td>
            <td></td>
        </tr>
        <tr>
            <td>dog</td>
            <td>brown</td>
            <td>no</td>
            <td>Spot</td>
            <td></td>
        </tr>
        <tr>
            <td>dog</td>
            <td>brown</td>
            <td>yes</td>
            <td>Rover</td>
            <td></td>
        </tr>
        <tr>
            <td>cat</td>
            <td>brown</td>
            <td></td>
            <td>Princess</td>
            <td></td>
        </tr>
    </table>
</html>

Примечание : Это проходит через дереводважды, но без расширения.Точное совпадение для желаемого результата без расширений потребовало бы имитировать механизм ключей следующим образом: пробежаться по дереву, добавив новые ключи (имя элемента плюс имена атрибутов) к параметру, затем снова для каждого ключа, проходящего через узел фильтрации дерева по ключу (может быть небольшая оптимизация, сохраняя набор узлов для несоответствующих элементов ...).В худшем случае (каждый узел с ключом distinc) пройдет через узел: N (для построения ключа) + (N + 1) * N / 2

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...