XSL Поиск элемента по атрибуту в for-each - PullRequest
2 голосов
/ 17 октября 2019

В моем XML-документе у меня есть следующие виды узлов:

<parent>
  <value id="value1" name="Y" type="number"/>
  <value id="value2" name="X" type="number"/>
  <value id="value3" name="Z" type="operation" op="-" args="value1;value2"/>
</parent>

И я хочу преобразовать это, чтобы полная операция выглядела так:

<parent>
  <value id="value1" name="Y" type="number" />
  <value id="value2" name="X" type="number" />
  <operation>
    <name>Z = Y - X</name>
  </operation>
</parent>

Iборюсь с моим шаблоном xsl. Вот полный код XSL:

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

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

    <xsl:template match="value[@type='operation']" priority="1">
        <xsl:variable name="name">
            <xsl:value-of select="concat(@name, ' = ')" />
            <xsl:for-each select="str:tokenize(@args, ';')">
                <xsl:choose>
                    <xsl:when test="//value[@id=current()]">
                        <xsl:value-of
                            select="concat(//value[@id=current()]/@name, ' ', @op, ' ')" />
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:value-of select="concat(current(), ' ', @op, ' ')" />
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:for-each>
        </xsl:variable>

        <operation>
            <name>
                <xsl:value-of select="$name" />
            </name>
        </operation>
    </xsl:template>

</xsl:stylesheet>

Внутри каждого переднего я проверяю, может ли он найти узел corrent, потому что иногда @args операции может быть, например, args="2.00;value1".

Очевидно, что что-то не так с моим тестом внутри for-each, потому что результат, который я получаю для входного файла, показанного выше:

<?xml version="1.0" encoding="UTF-8"?>
<parent>
    <value id="value1" name="Y" type="number" />
    <value id="value2" name="X" type="number" />
    <operation xmlns:str="http://exslt.org/strings">
        <name>Z = value1 value2  </name>
    </operation>
</parent>

Каким должен быть тест, чтобы получить имяправильного значения?

Ответы [ 2 ]

2 голосов
/ 17 октября 2019

С XSLT 1 и EXSLT str:tokenize вы можете использовать

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

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

    <xsl:key name="val-ref" match="value[@id]" use="@id"/>

    <xsl:template match="value[@type='operation']" priority="1">
        <xsl:variable name="op" select="."/>
        <xsl:variable name="name">
            <xsl:value-of select="concat(@name, ' = ')" />
            <xsl:for-each select="str:tokenize(@args, ';')">
                <xsl:if test="position() > 1">
                    <xsl:value-of select="concat(' ', $op/@op, ' ')"/>
                </xsl:if>
                <xsl:variable name="value" select="."/>
                <xsl:for-each select="$op">
                    <xsl:choose>                  
                        <xsl:when test="key('val-ref', $value)">
                            <xsl:value-of
                                select="key('val-ref', $value)/@name" />
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:value-of select="$value" />
                        </xsl:otherwise>
                    </xsl:choose>
                </xsl:for-each>
            </xsl:for-each>
        </xsl:variable>

        <operation>
            <name>
                <xsl:value-of select="$name" />
            </name>
        </operation>
    </xsl:template>

</xsl:stylesheet>

http://xsltransform.net/3MP2uCm

С XSLT 3 он становится более компактным:

<?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="#all"
    version="3.0">

  <xsl:key name="val-ref" match="value[@id]" use="@id"/>

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:template match="value[@op]">
      <operation>
          <name>
             <xsl:value-of select="@name || ' = '"/>
             <xsl:value-of select="tokenize(@args, ';') ! (key('val-ref', ., current()/ancestor::parent)/@name, .)[1]" 
             separator=" {@op} "/>
          </name>
      </operation>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty -development.net / jz1PuP6

2 голосов
/ 17 октября 2019

AFAICT, вы хотите сделать что-то вроде:

XSLT 1.0

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

<xsl:key name="arg-val" match="value" use="@id" />

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

<xsl:template match="value[@type='operation']">
    <xsl:variable name="arg1" select="substring-before(@args, ';')"/>
    <xsl:variable name="arg2" select="substring-after(@args, ';')"/>
    <xsl:variable name="val1" select="key('arg-val', $arg1)/@name" />
    <xsl:variable name="val2" select="key('arg-val', $arg2)/@name" />
    <operation>
        <name>
            <xsl:value-of select="@name"/>
            <xsl:text> = </xsl:text>
            <xsl:choose>
                <xsl:when test="$val1">
                    <xsl:value-of select="$val1"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="$arg1"/>
                </xsl:otherwise>
            </xsl:choose>
            <xsl:text> </xsl:text>
            <xsl:value-of select="@op"/>
            <xsl:text> </xsl:text>
            <xsl:choose>
                <xsl:when test="$val2">
                    <xsl:value-of select="$val2"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="$arg2"/>
                </xsl:otherwise>
            </xsl:choose>
        </name>
    </operation>
</xsl:template>

</xsl:stylesheet>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...