XSLT - сопоставлять элементы с одинаковыми именами тегов - PullRequest
0 голосов
/ 09 мая 2018

В качестве примера входного 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 голосов
/ 09 мая 2018
 <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>

Вы можете использовать этот xslt также

0 голосов
/ 09 мая 2018

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>

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>

ВЫВОД:

<?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>
0 голосов
/ 09 мая 2018

Уточнение точного ответа Рупеша с помощью ключа в XSLT 2 или 3, который вы можете использовать

  <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, который позволяет ограничить только поиск. к поддереву, но не к определенному уровню поддерева.

0 голосов
/ 09 мая 2018

Попробуйте это

<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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...