elementos distintos e agrupamento

votos
8

Dado o seguinte fragmento xml:

<Problems>
  <Problem>
    <File>file1</File>
    <Description>desc1</Description>
  </Problem>
  <Problem>
    <File>file1</File>
    <Description>desc2</Description>
  </Problem>
  <Problem>
    <File>file2</File>
    <Description>desc1</Description>
  </Problem>
</Problems>

Eu preciso produzir algo como

<html>
  <body>
    <h1>file1</h1>
    <p>des1</p>
    <p>desc2</p>
    <h1>file2</h1>
    <p>des1</p>
  </body>
</html>

Eu tentei usar uma chave, como

<xsl:key name=files match=Problem use=File/>

mas eu realmente não entendo como levá-la para a próxima etapa, ou se isso é mesmo a abordagem correta.

Publicado 30/12/2008 em 01:23
fonte usuário
Em outras línguas...                            


3 respostas

votos
5

Aqui está como eu faria isso, usando o método Muenchean. Google 'muenchean XSLT' para mais informações de pessoas mais inteligentes. Pode haver uma maneira inteligente, mas vou deixar isso para os outros.

Uma nota, I evitar o uso de capitais no início de nomes de elementos XML, por exemplo, 'File', mas isso é até você.

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="html"/>
    <xsl:key name="files" match="/Problems/Problem/File" use="./text()"/>
    <xsl:template match="/">
        <html>
            <body>
                <xsl:apply-templates select="Problems"/>
            </body>
        </html>
    </xsl:template>
    <xsl:template match="Problems">
        <xsl:for-each select="Problem/File[generate-id(.) = generate-id(key('files', .))]">
            <xsl:sort select="."/>
            <h1>
                <xsl:value-of select="."/>
            </h1>
            <xsl:apply-templates select="../../Problem[File=current()/text()]"/>
        </xsl:for-each>
    </xsl:template>
    <xsl:template match="Problem">
        <p>
            <xsl:value-of select="Description/text()"/>
        </p>
    </xsl:template>
</xsl:stylesheet>

A ideia é, chave cada elemento do arquivo usando seu valor de texto. Em seguida, exibir apenas os valores de arquivo se eles são o mesmo elemento como a chave. Para verificar se eles são o mesmo, use gerar-id. Há uma abordagem semelhante, onde você comparar o primeiro elemento que corresponda. Eu não posso dizer-lhe que é mais eficiente.

Eu testei o código aqui usando Marrowsoft Xselerator, minha ferramenta xslt favorito, embora já não está disponível, afaik. O resultado que eu tenho é:

<html>
<body>
<h1>file1</h1>
<p>desc1</p>
<p>desc2</p>
<h1>file2</h1>
<p>desc1</p>
</body>
</html>

Esta é usando msxml4.

Tenho ordenado a saída de arquivo. Eu não tenho certeza se você queria isso.

Eu espero que isso ajude.

Respondeu 30/12/2008 em 02:12
fonte usuário

votos
7

Esta solução é um pouco mais simples, mais eficiente e ao mesmo tempo mais geral do que o apresentado por Richard:

Esta transformação :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!--                                            -->
 <xsl:key name="kFileByVal" match="File"
       use="." />
<!--                                            -->
 <xsl:key name="kDescByFile" match="Description"
       use="../File"/>
<!--                                            -->
    <xsl:template match="/*">
     <html>
      <body>
      <xsl:for-each select=
         "*/File[generate-id()
                =
                 generate-id(key('kFileByVal',.)[1])]">
        <h1><xsl:value-of select="."/></h1>
        <xsl:for-each select="key('kDescByFile', .)">
          <p><xsl:value-of select="."/></p>
        </xsl:for-each>
      </xsl:for-each>
      </body>
     </html>
    </xsl:template>
</xsl:stylesheet>

quando aplicado ao documento XML fornecido :

<Problems>
    <Problem>
        <File>file1</File>
        <Description>desc1</Description>
    </Problem>
    <Problem>
        <File>file1</File>
        <Description>desc2</Description>
    </Problem>
    <Problem>
        <File>file2</File>
        <Description>desc1</Description>
    </Problem>
</Problems>

Produz o resultado desejado :

<html>
   <body>
      <h1>file1</h1>
      <p>desc1</p>
      <p>desc2</p>
      <h1>file2</h1>
      <p>desc1</p>
   </body>
</html>

Observe o padrão de jogo simples do primeiro <xsl:key>e como, usando um segundo <xsl:key>, podemos localizar todos os " Description" elementos que são irmãos de um " Fileelemento" que tem um determinado valor.

Poderíamos ter utilizado mais modelos em vez de <xsl:for-each>pull-processamento, no entanto, este é um caso muito simples ea solução realmente se beneficia de um código menor, mais compacto e mais legível.

Além disso, note, que em XSLT 2,0 um, tipicamente, utilizar a <xsl:for-each-group>instrução em vez do método Muenchian .

Respondeu 30/12/2008 em 07:45
fonte usuário

votos
0

Este XSLT 1.0 solução também irá fazer o truque. Pouco mais sucinto do que as outras soluções!

  <xsl:template match="/">           
    <html><body>
      <xsl:for-each select="//File[not(.=preceding::*)]">
        <h1><xsl:value-of select="." /></h1>
        <xsl:for-each select="//Problem[File=current()]/Description">
          <p><xsl:value-of select="." /></p>
        </xsl:for-each>
      </xsl:for-each>
    </body></html>
  </xsl:template>

Resultado:

<html xmlns="http://www.w3.org/1999/xhtml">
  <body>
    <h1>file1</h1>
    <p>desc1</p>
    <p>desc2</p>
    <h1>file2</h1>
    <p>desc1</p>
  </body>
</html>
Respondeu 19/09/2013 em 05:41
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more