简体   繁体   English

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

[英]XSLT - Match Elements with same Tag Name

As an input xml example I have 作为输入xml示例我有

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

Using a xslt rule I would like to obtain something like below 使用xslt规则我想获得类似下面的内容

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

Basically if the name of the child elements are the same I would like to append to the name the name of the first attribute. 基本上,如果子元素的名称相同,我想在名称后附加第一个属性的名称。

I found a lot of examples where you can match this but only when you know the name of the element. 我找到了很多可以匹配的示例,但只有当您知道元素的名称时才会这样。 Is there a way to match this kind of scenario without knowing the name of the element in advance For example: 有没有办法在不事先知道元素名称的情况下匹配这种情况例如:

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

will also return 也会回来

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

Thanks in advance for you help. 在此先感谢您的帮助。

Try this 尝试这个

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

For copy other data use identity transform as 对于复制其他数据,使用身份转换为

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

Refining Rupesh's fine answer with a key in XSLT 2 or 3 you can use 使用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 https://xsltfiddle.liberty-development.net/eiZQaF4/1

Although pondering it a bit longer it could give differents results than the accepted answer if there is an element that has children and grandchildren or further descendants of the same name as the third argument to the key function only allows restricting the search to a subtree but not to a certain level of a subtree. 虽然考虑它的时间稍长,如果有一个元素具有子孙,或者与key函数的第三个参数同名的后代只允许将搜索限制为子树而不是接受的答案,它可以给出不同的结果。到某个级别的子树。

Rupesh_Kr answer is very general and works in any situation. Rupesh_Kr答案非常通用,适用于任何情况。

However, if you want to use a specific xpath under which you only want to change the nodes based on your rule, then you can use the following stylesheet 但是,如果要使用特定的xpath ,您只想根据规则更改节点,则可以使用以下stylesheet

INPUT: 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>

STYLESHEET: 样式表:

<?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>

OUTPUT: OUTPUT:

<?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>
 <xsl:template match="Elem1">
        <xsl:element name="Elem1">
            <xsl:for-each select="Obj">
                <xsl:element name="{concat(local-name(),position())}">
                    <xsl:apply-templates select="@*"/>
                </xsl:element>
            </xsl:for-each>
        </xsl:element>
    </xsl:template>

You may use this xslt also 你也可以使用这个xslt

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM