XSLT: создание вложенных узлов с использованием преобразования одного узла - PullRequest
1 голос
/ 24 апреля 2020

Я хочу создать вложенные XML узлы, используя один узел из структуры XML, я пытался, но не смог вывести вложенную XML структуру. Могут ли некоторые помочь мне, я не могу получить ожидаемое поведение .

XML Структура:

<job[n]/> -> 0...n
<person-non-active/> -> 1
<removed> -> 1
<?xml version="1.0" encoding="UTF-8"?>
<root>
    <person>
        <job1/>
        <job2/>
        <person-non-active>
            <removed>
                <job3/>
                <job4/>
            </removed>
        </person-non-active>
    </person>
    <person>
      <bio>
        <job1/>
        <job2/>
        <person-non-active>
            <removed>
                <job3/>
            </removed>
        </person-non-active>
      </bio>
    </person>
   <person>
        <person-non-active>
            <removed>
                <job3/>
                <job4/>
            </removed>
        </person-non-active>
    </person>
</root>

XSL Используется:

<xsl:template match="person-non-active">
        <person>            
            <xsl:if test="(count(../*) &gt; 1)">
                <job>
                    <xsl:attribute name="status"><xsl:text>active</xsl:text></xsl:attribute>
                    <xsl:attribute name="jobs">
                        <xsl:for-each select="../*">
                            <xsl:if test="not(name() = 'person-non-active')">
                                <xsl:value-of select="name()"/><xsl:text>, </xsl:text>
                            </xsl:if>                            
                        </xsl:for-each>                
                    </xsl:attribute>
                </job>
            </xsl:if>
            <xsl:if test="(count(./removed/*) &gt; 0)">
                <job>
                    <xsl:attribute name="status"><xsl:text>non-acitve</xsl:text></xsl:attribute>
                    <xsl:attribute name="jobs">
                        <xsl:for-each select="./removed/*">
                            <xsl:value-of select="name()"/><xsl:text>, </xsl:text>
                        </xsl:for-each>                
                    </xsl:attribute>
                </job>
            </xsl:if>
        </person>
    </xsl:template>

Вывод получен:

<person>
 <job status="active" jobs="job1, job2, "/>
 <job status="non-acitve" jobs="job3, job4, "/>
</person>

Ожидаемый вывод

<person>
    <job status="active" jobs="job1, job2, ">
        <job status="non-acitve" jobs="job3, job4, "/>
    </job>
</person>
<person>
 <bio>
    <job status="active" jobs="job1, job2, ">
        <job status="non-acitve" jobs="job3, "/>
    </job>
 </bio>
</person>
<person>
    <job status="non-acitve" jobs="job3, job4, "/>
</person>

Элемент "персона-неактивен" должен использоваться для совпадения

Ответы [ 2 ]

1 голос
/ 24 апреля 2020

Не могли бы вы просто:

<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:template match="person">
    <person>
        <job status="active">
            <xsl:attribute name="jobs">
                <xsl:for-each select="*[not(self::person-non-active)]">
                    <xsl:value-of select="name()"/>
                    <xsl:text>, </xsl:text>
                 </xsl:for-each>                
            </xsl:attribute>
            <job status="non-active">
                <xsl:attribute name="jobs">
                    <xsl:for-each select="person-non-active/removed/*">
                        <xsl:value-of select="name()"/>
                        <xsl:text>, </xsl:text>
                     </xsl:for-each>                
                </xsl:attribute>
            </job>
        </job>
    </person>
</xsl:template>

</xsl:stylesheet>

Обратите внимание, что я изменил status="non-acitve" на status="non-active".


Добавлено в ответ на расширенный вопрос:

Это значительно сложнее, особенно если вы хотите сохранить элементы-оболочки активных заданий (например, bio в вашем примере), не зная заранее их имен.

Если job s являются только элементами без дочерних элементов вы можете сделать что-то вроде:

<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="*"/>

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

<!-- parent of active jobs -->
<xsl:template match="*[*[not(*)]]">
    <xsl:copy>
        <job status="active">
            <xsl:attribute name="jobs">
                <xsl:for-each select="*[not(*)]">
                    <xsl:value-of select="name()"/>
                    <xsl:text>, </xsl:text>
                 </xsl:for-each>                
            </xsl:attribute>
            <xsl:apply-templates select="*[*]"/>
        </job>  
    </xsl:copy>
</xsl:template>

<!-- ancestor of non-active jobs -->
<xsl:template match="person-non-active">
    <job status="non-active">
        <xsl:attribute name="jobs">
            <xsl:for-each select=".//*[not(*)]">
                <xsl:value-of select="name()"/>
                <xsl:text>, </xsl:text>
             </xsl:for-each>                
        </xsl:attribute>
    </job>  
</xsl:template>

</xsl:stylesheet>
1 голос
/ 24 апреля 2020

Вот как это можно сделать. Я думаю, что все, что не хватает, это разделители для значений в job/@jobs.

Я основал часть кода на этом ответе: Как применить функцию к последовательности узлов в XSLT

<?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="2.0">

  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="person">
      <root>
        <xsl:copy>
            <job>
                <xsl:attribute name="status" select="'active'"/>
                <xsl:variable name="seqj"><xsl:sequence select="for $j in *[starts-with(local-name(.),'job')] return local-name($j)"/></xsl:variable>
                <xsl:attribute name="jobs"><xsl:value-of select="$seqj"/></xsl:attribute>
                <job>
                    <xsl:attribute name="status" select="'non-active'"/>
                    <xsl:variable name="seqj"><xsl:sequence select="for $j in person-non-active/removed/*[starts-with(local-name(.),'job')] return local-name($j)"/></xsl:variable>
                    <xsl:attribute name="jobs"><xsl:value-of select="$seqj"/></xsl:attribute>
                </job>
            </job>
        </xsl:copy>
      </root>
  </xsl:template>

</xsl:stylesheet>

Вы можете увидеть, как это работает здесь: https://xsltfiddle.liberty-development.net/pNmC4J3

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