首页 > 解决方案 > XSLT - 重命名具有特定属性的所有子元素

问题描述

我有以下 XML 文档

<root>
  <List1 array="true">
    <Item>
      <Value1>405</Value1>
      <Value2>Text</Value2>
      <List2 array="true">
        <Item>123</Item>
        <Item>345</Item>
      </List2>
      <List3 array="true">
        <Item>
          <Value1>a</Value1>
          <Value2>b</Value2>
        </Item>
      </List3>
      <Value3>21956CB</Value3>
    </Item>
  </List1>
</root>

我想将<Item>具有array属性的元素的所有子元素重命名为父元素的名称。目标 XML 应如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <List1 array="true"/>
  <List1>
    <Value1>405</Value1>
    <Value2>Text</Value2>
    <List2 array="true"/>
    <List2>123</List2>
    <List2>345</List2>
    <List3 array="true"/>
    <List3>
      <Value1>a</Value1>
      <Value2>b</Value2>
    </List3>
    <Value3>21956CB</Value3>
  </List1>
</root>

我的 XSLT 仅适用于第一个元素的 Items <List1><List2>and将<List3>被忽略,以便我的 XSLTis 的结果:

错误的结果:

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <List1 array="true"/>
  <List1>
    <Value1>405</Value1>
    <Value2>Text</Value2>
    <List2 array="true">
      <Item>123</Item>
      <Item>345</Item>
    </List2>
    <List3 array="true">
      <Item>
        <Value1>a</Value1>
        <Value2>b</Value2>
      </Item>
    </List3>
    <Value3>21956CB</Value3>
  </List1>
</root>

XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output encoding="UTF-8" media-type="text/xml" method="xml"/>

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

  <xsl:template match="//*[@array]">

    <xsl:if test="count(./Item) = 1">
      <xsl:element name="{name(.)}">
        <xsl:attribute name="array">true</xsl:attribute>
      </xsl:element>
    </xsl:if>

    <xsl:for-each select="./Item">
      <xsl:element name="{name(..)}">
        <xsl:choose>
          <!-- is the current node just a text without child then use just the text -->
          <xsl:when test="count(./*) = 0">
            <xsl:copy-of select="text()"/>
          </xsl:when>
          <!-- otherwise copy all childs except the null values -->
          <xsl:otherwise>
            <xsl:copy-of select="./*"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:element>
    </xsl:for-each>

  </xsl:template>
</xsl:stylesheet>

我的 XSLT 有什么问题?我的 XSLT 知识不太好,我认为我遗漏了一些明显的东西。

我希望你能帮助我。

标签: xmlxslt

解决方案


请尝试以下 XSLT 2.0 样式表

<?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:output omit-xml-declaration="yes" indent="yes"/>
  <xsl:strip-space elements="*"/>  
  <xsl:template match="node() | @*">
    <xsl:copy>
      <xsl:apply-templates select="node() | @*"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="*[@array]/Item">
    <xsl:variable name="name" select="parent::*/name(.)"/>
    <xsl:element name="{$name}">
      <xsl:apply-templates/>
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

它使用 Saxon HE/PE/EE 9.9.1.7 进行了测试。这是一个解释

首先使用身份模板将输入原封不动地复制到输出中。

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

然后将所有<Item>具有父元素的元素与@array属性匹配,获取父元素的名称,并在新元素中使用该名称。

<xsl:template match="*[@array]/Item">
  <xsl:variable name="name" select="parent::*/name(.)"/>
  <xsl:element name="{$name}">
    <xsl:apply-templates/>
  </xsl:element>
</xsl:template>

推荐阅读