XSLT:重命名重复的标签

时间:2019-03-20 14:21:15

标签: xml xslt

我有以下输入。

XML输入

<DOC>
    <ID>1234</ID>
    <TXT>
        <A><DESC type="PERSON">George Washington</DESC> lived in a house called <DESC type="PLACE">Mount Vernon.</DESC></A>
    </TXT>
</DOC>

然后我应用以下XSLT

XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:template match="/">
    <DOC>
      <xsl:for-each select="DOC/*">
       <xsl:copy>
        <xsl:value-of select="current()"/>
       </xsl:copy>
      </xsl:for-each>
   <xsl:apply-templates select="DOC/TXT"/>
   </DOC>
  </xsl:template>
  <xsl:template match="TXT">
  <xsl:element name="TXT">
    <xsl:for-each select="S">
      <xsl:element name="{local-name()}">
        <xsl:for-each select="*">
          <xsl:variable name="type" select="@type"/>
          <xsl:element name="{concat(name(), '_', $type)}">
          <xsl:value-of select="current()"/>
        </xsl:element>
        </xsl:for-each>
      </xsl:element>
    </xsl:for-each>
  </xsl:element>
  </xsl:template>
</xsl:stylesheet>

由此产生此输出。

输出

<DOC>
  <ID>1234</ID>
  <TXT>
    George Washington lived in a house called Mount Vernon.
  </TXT>
  <TXT>
    <A>
      <DESC_PERSON>George Washington</DESC_PERSON> 
      <DESC_PLACE>Mount Vernon</DESC>
    </A>
  </TXT>
</DOC>

这几乎正是我想要的,但是,我需要将第一个TXT标签重命名为RAW_TXT,因为稍后我需要在NiFi中使用Avro架构,并且当该架构具有重复标签时会引发错误。我试图将下面的代码添加到XSLT中,但是它只是创建两个相同的raw_txt字段,并删除了DESC_PERSONDESC_PLACE

尝试

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

2 个答案:

答案 0 :(得分:0)

如果您有两个与TXT匹配的模板,那么这将被视为XSLT中的错误。有些处理器会标记错误,而其他处理器只会使用最后一个匹配的模板。

要区分它们,可以使用“模式”。您还需要将现有的xsl:copy更改为xsl:apply-templates,以允许使用非模式模板。

尝试使用此XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

  <xsl:output method="xml" indent="yes" />

  <xsl:template match="/">
    <DOC>
      <xsl:apply-templates select="DOC/*" />
      <xsl:apply-templates select="DOC/TXT" mode="extra"/>
   </DOC>
  </xsl:template>

  <xsl:template match="*">
    <xsl:copy>
      <xsl:value-of select="current()"/>
     </xsl:copy>
  </xsl:template>

  <xsl:template match="TXT">
    <RAW_TXT>
      <xsl:value-of select="current()"/>
     </RAW_TXT>
  </xsl:template>  

  <xsl:template match="TXT" mode="extra">
  <TXT>
    <xsl:for-each select="*">
      <xsl:element name="{local-name()}">
        <xsl:for-each select="*">
          <xsl:variable name="type" select="@type"/>
          <xsl:element name="{concat(name(), '_', $type)}">
          <xsl:value-of select="current()"/>
        </xsl:element>
        </xsl:for-each>
      </xsl:element>
    </xsl:for-each>
  </TXT>
  </xsl:template>
</xsl:stylesheet>

答案 1 :(得分:0)

首先,您希望两个模板具有相同的模式(每个TXT元素)。这是modes的用例,因为@Tim C回答了。

第二,您要处理所有内容(甚至是TXT元素),然后重新处理TXT元素以去除标记。这是named templated作为身份转换以及在任何模式中使用built-in rules的很好用例。

您的输入与此样式表:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:strip-space elements="*"/>
  <xsl:output indent="yes"/>
  <xsl:template match="@*|node()" name="copy">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="TXT">
    <xsl:call-template name="copy"/>
    <RAW_TXT>
      <xsl:apply-templates mode="text"/>
    </RAW_TXT>
  </xsl:template>
</xsl:stylesheet>

结果:

<DOC>
   <ID>1234</ID>
   <TXT>
      <A>
         <DESC type="PERSON">George Washington</DESC> lived in a house called <DESC type="PLACE">Mount Vernon.</DESC>
      </A>
   </TXT>
   <RAW_TXT>George Washington lived in a house called Mount Vernon.</RAW_TXT>
</DOC>