Не получается преобразовать xml через xslt

Есть xml:

<recordCollection>
  <record>
    <Object>
      <Item>
        <Name>Наименование1</Name>
      </Item>
    </Object>
    <IPv4>
      <Item>
        <IPадресV4>IPv4-адрес1111</IPадресV4>
        <ID>3444</ID>
      </Item>
      <Item>
        <IPадресV4>IPv4-адрес2222</IPадресV4>
        <ID>3445</ID>
      </Item>
    </IPv4>
    <IPv6>
      <Item>
        <IPадрес>IPv6-адрес111</IPадрес>
        <ID>3444</ID>
      </Item>
      <Item>
        <IPадрес>IPv6-адрес222</IPадрес>
        <ID>3445</ID>
      </Item>
    </IPv6>
  </record>
</recordCollection>

Нужно преобразовать его вот в такой:

<alert>
  <object>Наименование1</object>
  <rows>
    <row>
      <IPv4>IPv4-адрес1111</IPv4>
      <IPv6> IPv6-адрес1111</IPv6>
    </row>
    <row>
      <IPv4>IPv4-адрес2222</IPv4>
      <IPv6> IPv6-адрес222</IPv6>
    </row>
  </rows>
</alert>

Как это сделать с помощью xslt?

с помощью нейронки смог сделать такой xslt:

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

    <!-- Главная инструкция -->
    <xsl:template match="/recordCollection">
        <alert>
            <!-- Наименование объекта -->
            <object>
                <xsl:value-of select="record/Object/Item/Name"/>
            </object>
            
            <!-- Таблица с объединенными данными -->
            <rows>
                <!-- Цикл по всем Item'ам в блоке IPv4 -->
                <xsl:for-each select="record/IPv4/Item">
                    <xsl:variable name="currentId" select="ID"/>
                    
                    <!-- Строка таблицы -->
                    <row>
                        <!-- IPv4 -->
                        <IPv4>
                            <xsl:value-of select="IPадресV4"/>
                        </IPv4>
                        
                        <!-- Найти соответствующее значение IPv6 по ID -->
                        <xsl:variable name="ipv6Node" select="../../IPv6/Item[ID = $currentId]"/>
                        
                        <!-- Если нашли, то вывести IPv6 -->
                        <xsl:if test="$ipv6Node">
                            <IPv6>
                                <xsl:value-of select="$ipv6Node/IPадрес"/>
                            </IPv6>
                        </xsl:if>
                    </row>
                </xsl:for-each>
            </rows>
        </alert>
    </xsl:template>

</xsl:stylesheet>

Минус в том, что этот вариант фиксируется на количестве item-ов ipv4,но в моём случае ipv4 может быть 1, а ipv6 два три и т.д. .


Ответы (1 шт):

Автор решения: Yitzhak Khabinsky

в моём случае ipv4 может быть 1, а ipv6 два три и т.д.

Как это должно выглядеть если у вас 1 ipv4 и 3 ipv6? Или наоборот, 3 ipv4 и 1 ipv6?

Mинимальный, самодостаточный и воспроизводимый пример для всех возможных сценариев отсутствует. Так что я стреляю с бедра.

Попробуйте следующее решение.

Мы используем "Itemlist" индекс, указанный элементом <xsl:key> для всего XML-документа. Таким образом не имеет значения сколько XML элементов IPv4 или IPv6.

Переменная $currentId содержит уникальные значения ID идентификаторов в цикле.

Исходный XML

<recordCollection>
    <record>
        <Object>
            <Item>
                <Name>Наименование1</Name>
            </Item>
        </Object>
        <IPv4>
            <Item>
                <IPадресV4>IPv4-адрес1111</IPадресV4>
                <ID>3444</ID>
            </Item>
            <Item>
                <IPадресV4>IPv4-адрес2222</IPадресV4>
                <ID>3445</ID>
            </Item>
        </IPv4>
        <IPv6>
            <Item>
                <IPадрес>IPv6-адрес111</IPадрес>
                <ID>3444</ID>
            </Item>
            <Item>
                <IPадрес>IPv6-адрес222</IPадрес>
                <ID>3445</ID>
            </Item>
            <Item>
                <IPадрес>IPv6-адрес222a</IPадрес>
                <ID>3445</ID>
            </Item>
            <Item>
                <IPадрес>IPv6-адрес333</IPадрес>
                <ID>3770</ID>
            </Item>
        </IPv6>
    </record>
</recordCollection>

XSLT 1.0

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes" encoding="utf-8"
                omit-xml-declaration="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:key name="Itemlist" match="Item" use="ID"/>

    <xsl:template match="/recordCollection">
        <alert>
            <!-- Наименование объекта -->
            <object>
                <xsl:value-of select="record/Object/Item/Name"/>
            </object>
            <!-- Таблица с объединенными данными -->
            <rows>
                <xsl:for-each select="//Item[count(. | key('Itemlist',ID)[1]) = 1][ID]">
                    <xsl:variable name="currentId"
                                  select="ID"/>
                    <row>
                        <xsl:for-each select="//Item[ID=$currentId]">
                            <xsl:if test="IPадресV4">
                                <IPv4>
                                    <xsl:value-of select="IPадресV4"/>
                                </IPv4>
                            </xsl:if>
                            <xsl:if test="IPадрес">
                                <IPv6>
                                    <xsl:value-of select="IPадрес"/>
                                </IPv6>
                            </xsl:if>
                        </xsl:for-each>
                    </row>
                </xsl:for-each>
            </rows>
        </alert>
    </xsl:template>
</xsl:stylesheet>

Выходной XML

<alert>
  <object>Наименование1</object>
  <rows>
    <row>
      <IPv4>IPv4-адрес1111</IPv4>
      <IPv6>IPv6-адрес111</IPv6>
    </row>
    <row>
      <IPv4>IPv4-адрес2222</IPv4>
      <IPv6>IPv6-адрес222</IPv6>
      <IPv6>IPv6-адрес222a</IPv6>
    </row>
    <row>
      <IPv6>IPv6-адрес333</IPv6>
    </row>
  </rows>
</alert>
→ Ссылка