使用XSLT

时间:2019-02-04 09:59:42

标签: xml xslt transformation

你好Stackoverflowers-

这是我第一次发帖。我遇到了一个令人沮丧的XSLT问题,尽管提出了各种设想的解决方案,但仍未能解决。该方案是将一个XML文档(#1)基本转换为另一个(#2)格式,以实现两个系统之间的互操作性。

XML文档#1包含许多重复的元素,每个元素包含一个单独的值。这些重复的元素需要转换为doc#2的替代元素,并包含来自doc#1的相应值。

尽管进行了大量实验,但我似乎无法使它正常工作。 doc#1中的相关元素如下:

 <mods:extension> 
  <rx:funder>Funder A</rx:funder> 
  <rx:projectid>Funder A code number</rx:projectid> 
  <rx:funder>Funder B</rx:funder> 
  <rx:projectid>Funder B code number</rx:projectid> 
 </mods:extension> 

需要到XML中以在文档#2中进行转换,如下所示:

<project_input>
  <item>
    <project>Funder A code number</project>
    <funder_name>Funder A</funder_name>
  </item>
  <item>
    <project>Funder B code number</project>
    <funder_name>Funder B</funder_name>
  </item>
</project_input>

但不幸的是,我的转换输出始终是以下内容的变体:

<project_input>
  <item>
    <project>Funder A code numberFunder B code number</project>
    <funder_name>Funder AFunder B</funder_name>
  </item>
</project_input>

...将值填充到单个元素中。

目前的转换是这样的:

<xsl:if test="v3:extension">
<project_input>
<item>      
<xsl:for-each select="v3:extension/rx:projectid">
    <project>
        <xsl:value-of select="."/>   
    </project>
</xsl:for-each> 
<xsl:for-each select="v3:extension/rx:funder">
    <funder_name>
        <xsl:value-of select="."/>      
    </funder_name>
</xsl:for-each>
</item> 
</project_input>
</xsl:if>

因此,问题在于,尽管正确的值在doc#1中成功循环,但在doc#2中没有正确输出。

我尝试使用合并了不同值的变体,但这也不成功。令人沮丧的是,我以前做过这样的事情,但我一辈子都记不起来了!堆栈上也已经描述了类似的场景,但是在那里提出的解决方案似乎在这种用例中不起作用。

有人可以指出我所缺少的明显东西吗?谢谢

已更新问题-请参阅下面的评论。这需要在XSLT 1.0中工作。我设法把一些东西凑齐了。只要提取,排序和分组了正确的数据,它就可以正常工作,但是缺少指定的XML元素。我可能已经忽略了一些很明显的东西。

有人可以帮助我吗?这是XSLT 1.0:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:mods="http://www.loc.gov/mods/v3" xmlns:rx="http://example.com/rx" 
version="1.0">
<xsl:output indent="yes"/>
<xsl:key use="rx:funder" match="mods:mods/mods:extension" name="groups" />
<xsl:template match="/">
<xsl:apply-templates select="mods:mods/mods:extension" />
</xsl:template>
<xsl:template match="mods:mods">
<rioxx2_project_output>
  <xsl:for-each select="/mods:extension[generate-id(.)=generate- 
  id(key('groups', rx:funder))]">
    <item>
      <funder_name>
        <xsl:value-of select="mods:extension/rx:funder/text()" />
      </funder_name>
      <project>
        <xsl:value-of select="mods:extension/rx:projectid/text()" />
      </project>
     </item>
     </xsl:for-each>
     </project_input>
   </xsl:template>
  </xsl:stylesheet>

这里在XSLT小提琴中:https://xsltfiddle.liberty-development.net/bFN1y8S/1

2 个答案:

答案 0 :(得分:1)

这似乎是使用for-each-group group-starting-withgroup-ending-with的经典示例,因此在您的情况下,例如使用

  <xsl:template match="mods:extension">
      <project_input>
          <xsl:for-each-group select="*" group-starting-with="rx:funder">
              <item>
                  <project>
                      <xsl:value-of select="current-group()[2]"/>
                  </project>
                  <funder_name>
                      <xsl:value-of select="."/>
                  </funder_name>
              </item>
          </xsl:for-each-group>
      </project_input>
  </xsl:template>

假设mods:extension的子元素的序列具有一定规律性,rx:funder可以识别组。

使用XSLT 3的完整示例https://xsltfiddle.liberty-development.net/bFN1y8S(对于XSLT 2处理器,您需要拼写身份转换模板而不是使用xsl:mode

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

  <xsl:output indent="yes"/>

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:template match="mods:extension">
      <project_input>
          <xsl:for-each-group select="*" group-starting-with="rx:funder">
              <item>
                  <project>
                      <xsl:value-of select="current-group()[2]"/>
                  </project>
                  <funder_name>
                      <xsl:value-of select="."/>
                  </funder_name>
              </item>
          </xsl:for-each-group>
      </project_input>
  </xsl:template>

</xsl:stylesheet>

要在XSLT 1中实现类似的方法,一种方法是使用一个密钥,该密钥在前一个兄弟rx:funder的id上键入任何非rx:funder元素:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:mods="http://example.com/mods"
    xmlns:rx="http://example.com/rx"
    exclude-result-prefixes="mods rx"
    version="1.0">

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

  <xsl:key name="group" match="mods:extension/*[not(self::rx:funder)]" 
     use="generate-id(preceding-sibling::rx:funder[1])"/>

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

  <xsl:template match="mods:extension">
      <project_input>
          <xsl:for-each select="rx:funder">
              <item>
                  <project>
                      <xsl:value-of select="key('group', generate-id())[self::rx:projectid]"/>
                  </project>
                  <funder_name>
                      <xsl:value-of select="."/>
                  </funder_name>
              </item>
          </xsl:for-each>
      </project_input>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/bFN1y8S/3

答案 1 :(得分:0)

    <xsl:strip-space elements="*"/>
        <xsl:template match="node()">
            <xsl:copy>
                <xsl:apply-templates />
            </xsl:copy>
        </xsl:template>
        <xsl:template match="mods:extension">
            <xsl:element name="project_input">
                <xsl:for-each-group select="node()" group-starting-with="rx:funder">

                       <item>
                           <project>
                               <xsl:value-of select="./following-sibling::*[1][self::rx:projectid]"/>
                       </project>
                           <funder_name>
                               <xsl:value-of select="."/>
                           </funder_name>

                       </item>

                </xsl:for-each-group>
            </xsl:element>  
        </xsl:template>
Kindly chek it.