使用XSLT根据位置对齐字段

时间:2013-07-31 16:01:35

标签: xml xslt translation xslt-2.0 xpath-3.0

我有一个XML文档,其中的记录包含英语和西班牙语主题标签的单独字段。单个标签以分号分隔。

<collections>
  <collection name="anyCollection">
    <record>
      <field name="materia">comida; bebida; fiesta</field>
      <field name="subject">food; drink; party</field>
      <field name="recordid">abc0001</field>
    </record>
    <record>
      <field name="materia">comida; bebida; fiesta</field>
      <field name="subject">food; drink; party</field>
      <field name="recordid">abc0002</field>
    </record>
    <record>
      <field name="materia">comida; bebida; fiesta</field>
      <field name="subject">food; drink; party</field>
      <field name="recordid">abc0003</field>
    </record>
    <record>
      <field name="materia">fiesta; sombreros; música; baile; agua; cerveza; sopa</field>
      <field name="subject">party; hats; music; dance; water; beer; soup</field>
      <field name="recordid">abc0004</field>
    </record>
    <record>
      <field name="materia">comida; bebida; fiesta; sombreros; música</field>
      <field name="subject">food; drink; party; hats; music</field>
      <field name="recordid">abc0005</field>
    </record>
    <record>
      <field name="materia">comida; bebida; cerveza; agua</field>
      <field name="subject">food; drink; beer; water</field>            
      <field name="recordid">abc0006</field>
    </record>        
    <record>
      <field name="materia">fiesta; sombreros; música; baile; agua; cerveza</field>
      <field name="subject">party; hats; music; dance; water; beer</field>
      <field name="recordid">abc0007</field>
    </record>       
  </collection>
</collections>

我希望能够输出一个文本文件,其中两个字段的内容按位置分组和对齐,以便我可以确定它们是彼此的镜像。这是我目前的样式表。它产生我想要的基本输出,但它不是动态的。基本上,我希望能够按位置迭代每个字段的内容。我猜我需要某种递归模板或函数,但我无法搞清楚。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs"    
  version="2.0">

  <xsl:variable name="field">
    <xsl:for-each  
      select="collections/collection[@name='anyCollection']/record">
      <record>
        <xsl:for-each select="field">
          <field>
            <xsl:for-each select="tokenize(.[@name='materia'],';')">
              <materia>
                <xsl:value-of select="."/>
              </materia>
            </xsl:for-each>
            <xsl:for-each select="tokenize(.[@name='subject'],';')">
              <subject>
                <xsl:value-of select="."/>
              </subject>
            </xsl:for-each>
          </field>
        </xsl:for-each>
      </record>
    </xsl:for-each>
  </xsl:variable>

  <xsl:variable name="align">        
    <xsl:for-each select="$field/record/field">           
      <languagePair1>                
        <xsl:for-each select="materia[1]">                    
          <xsl:value-of select="."/> 
          <xsl:text>_</xsl:text>    
        </xsl:for-each>
        <xsl:for-each select="subject[1]">                    
          <xsl:value-of select="."/> 
          <xsl:text>&#10;</xsl:text>    
        </xsl:for-each>
      </languagePair1>
      <languagePair2>
        <xsl:for-each select="materia[2]">                    
          <xsl:value-of select="."/> 
          <xsl:text>_</xsl:text>    
        </xsl:for-each>                                               
        <xsl:for-each select="subject[2]">                    
          <xsl:value-of select="."/> 
          <xsl:text>&#10;</xsl:text>    
        </xsl:for-each>                             
      </languagePair2>
    </xsl:for-each>
  </xsl:variable>

  <xsl:template match="/">             
    <xsl:for-each-group select="$align/languagePair1" group-by=".">           
      <xsl:value-of select="current-grouping-key()"/>           
    </xsl:for-each-group>
    <xsl:for-each-group select="$align/languagePair2" group-by=".">           
      <xsl:value-of select="current-grouping-key()"/>            
    </xsl:for-each-group>
  </xsl:template>

</xsl:stylesheet>

这是我想要的基本输出:

comida_food

bebida_drink

fiesta_party

sombreros_hats

música_music

我还需要输出与每个标记关联的recordid,但我还没有将它包含在样式表中。

添加了这些信息后,所需的输出将如下所示:

comida_food
abc0001
abc0002
abc0003
abc0005
abc0006

bebida_drink
abc0001
abc0002
abc0003
abc0005
abc0006

fiesta_party
abc0001
abc0002
abc0003
abc0004
abc0005
abc0007

sombreros_hats
abc0004
abc0005
abc0007

música_music
abc0004
abc0005
abc0007

3 个答案:

答案 0 :(得分:1)

XPath 3.0中fn:for-each-pair的一个很好的用例:

for-each-pair(
  tokenize($materia, '; '), 
  tokenize($subject, '; '),
  function($x, $y) { $x || '_' || $y || '&#xa;' })

可在Saxon-PE 9.5.1.1中使用。

答案 1 :(得分:1)

在XPath 3.0中使用带有Saxon-PE 9.5.1.1的fn:for-each-pair,所需的输出由以下样式表生成:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="3.0">

  <xsl:output method="text"/>
  <xsl:template match="/">
    <xsl:for-each-group select="collections/collection[@name = 'anyCollection']/record"
      group-by="for-each-pair(
      tokenize(field[@name = 'materia'], '; '), 
      tokenize(field[@name = 'subject'], '; '),
      function($x, $y) { $x || '_' || $y || '&#xa;' })">

      <xsl:value-of select="current-grouping-key()"/>

      <xsl:for-each-group select="current-group()" group-by="field[@name='recordid']">
        <xsl:sort select="substring(translate(current-grouping-key(),'ÁÉÍÓÚÜáéíóúü','AEIOUUaeiouu'),4)" data-type="number"/>                
        <xsl:value-of select="current-grouping-key()"/>
        <xsl:text>&#10;</xsl:text>    
      </xsl:for-each-group>

      <xsl:text>&#10;</xsl:text>                       
    </xsl:for-each-group>
  </xsl:template>
</stylesheet>

答案 2 :(得分:0)

我可以使用以下代码获得所需的输出:

<xsl:stylesheet version="2.0" exclude-result-prefixes="xs"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">


<xsl:output method="text"/>

<xsl:template match="/">
  <xsl:for-each-group select="collections/collection[@name = 'anyCollection']/record" group-by="tokenize(field[@name = 'materia'], '; ')">
    <xsl:variable name="pos" select="position()"/>
    <xsl:variable name="subjects" select="tokenize(field[@name = 'subject'], '; ')"/>
    <xsl:value-of select="concat(current-grouping-key(), '_', $subjects[$pos]), current-group()/field[@name = 'recordid']" separator="&#10;"/>
    <xsl:text>&#10;&#10;</xsl:text>
  </xsl:for-each-group>
</xsl:template>

</xsl:stylesheet>

这样就够了吗?我不确定您希望代码以何种方式呈现动态,我假设您知道您感兴趣的name元素的field属性(即materiasubject )。