XSLT - 匹配具有相同标记名称的元素

时间:2018-05-09 06:12:49

标签: xml xslt xsd

作为输入xml示例我有

<Elem1>
    <Obj name="1">
    <Obj name="2">
    <Obj name="3">
</Elem1>

使用xslt规则我想获得类似下面的内容

<Elem1>
    <Obj1 name="1">
    <Obj2 name="2">
    <Obj3 name="3">
</Elem1>

基本上,如果子元素的名称相同,我想在名称后追加第一个属性的名称。

我发现很多例子可以匹配,但只有当你知道元素的名称时才会这样。 有没有办法在不事先知道元素名称的情况下匹配这种情况 例如:

<Elem1>
    <Second name="1">
    <Second name="2">
    <Second name="3">
</Elem1> 

也将返回

    <Elem1>
        <Second1 name="1">
        <Second2 name="2">
        <Second3 name="3">
    </Elem1> 

先谢谢你的帮助。

4 个答案:

答案 0 :(得分:3)

试试这个

<xsl:template match="*[@name][count(../*[name() = current()/name()]) > 1]">
    <xsl:element name="{name()}{@name}">
        <xsl:apply-templates select="node()|@*"/>
    </xsl:element>
</xsl:template>

复制其他数据时使用身份转换为

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

答案 1 :(得分:3)

使用XSLT 2或3中的密钥完善Rupesh的精细答案你可以使用

  <xsl:key name="group-by-name" match="*[@name]" use="node-name(.)"/>

  <xsl:template match="*[@name and key('group-by-name', node-name(.), ..)[2]]">
      <xsl:element name="{name()}{@name}" namespace="{namespace-uri()}">
          <xsl:apply-templates select="@* | node()"/>
      </xsl:element>
  </xsl:template>

https://xsltfiddle.liberty-development.net/eiZQaF4/1

虽然考虑它的时间稍长,如果有一个元素有子孙,或者与key函数的第三个参数同名的后代只允许限制,它可以给出不同于已接受答案的结果。搜索到子树但不到某个子树级别。

答案 2 :(得分:2)

Rupesh_Kr答案非常笼统,适用于任何情况。

但是,如果您想使用特定的xpath,您只想根据规则更改节点,那么您可以使用以下stylesheet

<强> INPUT:

<?xml version="1.0"?>
<Elems>
  <Elem>
    <Obj1 name="1"/>
    <Obj2 name="2"/>
    <Obj3 name="3"/>
  </Elem>
  <Elem>
    <Second name="1"/>
    <Second name="2"/>
    <Second name="3"/>
  </Elem>
</Elems>

<强>样式表:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:redirect="http://xml.apache.org/xalan/redirect" xmlns:xalan="http://xml.apache.org/xslt" version="1.0" extension-element-prefixes="redirect">
  <xsl:output method="xml" indent="yes" xalan:indent-amount="4"/>
  <xsl:strip-space elements="*"/>
  <xsl:template match="node() | @*">
    <xsl:copy>
      <xsl:apply-templates select="node() | @*"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="/Elems/Elem"> <!-- only for those nodes you do a specific process -->
    <!-- variable to store the name of the first child -->
    <xsl:variable name="subNodeName" select="local-name(./*[1])"/>
    <!-- variable to store the number of children -->
    <xsl:variable name="countNodes" select="count(./*)"/>
    <xsl:choose>
      <xsl:when test="$countNodes=count(./*[local-name()=$subNodeName])"><!--when all children have the same name -->
        <Elem>
          <xsl:for-each select="./*">
            <xsl:element name="{name()}{@name}">
              <xsl:apply-templates select="node() | @*"/>
            </xsl:element>
          </xsl:for-each>
        </Elem>
      </xsl:when>
      <xsl:otherwise>
        <Elem>
          <xsl:apply-templates select="node() | @*"/>
        </Elem>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

<强>输出:

<?xml version="1.0"?>
<Elems>
  <Elem>
    <Obj1 name="1"/>
    <Obj2 name="2"/>
    <Obj3 name="3"/>
  </Elem>
  <Elem>
    <Second1 name="1"/>
    <Second2 name="2"/>
    <Second3 name="3"/>
  </Elem>
</Elems>

答案 3 :(得分:0)

warning

您也可以使用此xslt