XSLT: получить элемент с определенным вложенным элементом без текста - PullRequest
0 голосов
/ 15 марта 2019

Привет У меня есть XML-файл, как показано ниже:

<root>
    <body>
        <place attr="1" id="1" ref="www.example.com">
            <name>abc
            </name>
        </place>
        <place attr="1" id="2" ref="www.example.com">
            <name>def
            </name>
        </place>
        <place attr="2" id="3">
            <place attr="3" id="4" ref="www.example.com">
                <name>efg
                </name>
            </place>
        </place>
    </body>
</root>

Я хочу получить все элементы <place>, которые имеют дочерние элементы <name> и без текста между элементами.

Что мне нужно в выводе, это что-то вроде:

<root>
   <place attr="1" id="1" ref="www.example.com" />
   <place attr="1" id="2" ref="www.example.com" />
   <place attr="3" id="4" ref="www.example.com" />
</root>

Мой xsl-код, который возвращает все теги <place> (что мне не нужно) + текст между тегами <name>:

   <xsl:template match="root">       
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates/> 
        </xsl:copy>
    </xsl:template>

   <xsl:template match="place">       
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates/> 
        </xsl:copy>
    </xsl:template>

xml output:

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <place attr="1" id="1" ref="www.example.com">abc</place>
   <place attr="1" id="2" ref="www.example.com">def</place>
   <place attr="2" id="3">
      <place attr="3" id="4" ref="www.example.com">efg</place>
   </place>
</root>

Ответы [ 3 ]

1 голос
/ 15 марта 2019

Чтобы получить запрошенный вывод, вы можете просто:

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:template match="/root">
    <xsl:copy>
        <xsl:for-each select=".//place[name]">
            <xsl:copy>
                <xsl:copy-of select="@*"/>
            </xsl:copy>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>
1 голос
/ 15 марта 2019

Во-первых, вам нужно понять модель процесса XSLT : начиная с корня документа входного документа, процессор пытается сопоставить этот узел с некоторым правилом, описываемым шаблоном некоторого шаблона. Существует несколько встроенных правил : в основном они проходят уровень за уровнем во входном документе и от первого до последнего в порядке документов.

Вот почему вам нужно правило для копирования root и place элементов и одно правило для не выводить текстовые узлы.

С этим входом

<root>
    <body>
        <place attr="1" id="1" ref="www.example.com">
            <name>abc
            </name>
        </place>
        <place attr="1" id="2" ref="www.example.com">
            <name>def
            </name>
        </place>
        <place attr="2" id="3">
            <place attr="3" id="4" ref="www.example.com">
                <name>efg
                </name>
            </place>
        </place>
    </body>
</root>

Эта таблица стилей

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="root|place[name]">
        <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="text()"/>
</xsl:stylesheet>

выход

<root>
    <place attr="1" id="1" ref="www.example.com" />
    <place attr="1" id="2" ref="www.example.com" />
    <place attr="3" id="4" ref="www.example.com" />
</root>
1 голос
/ 15 марта 2019

Ключом является ваше заявление apply-templates.

Как вы утверждаете, вы "хотите получить только тег <place>, который имеет в качестве ребенка <name>" .Таким образом, вы должны выбрать только эти.

Ваш текущий код:

<xsl:template match="root">
    <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates/>
    </xsl:copy>
</xsl:template>

Когда вы используете просто <xsl:apply-templates/>, это то же самое, что сказать "применить шаблоны к * всему * под этим ".Таким образом, вы ловите даже те элементы <place>, которые вам не нужны.

Чтобы быть более избирательным и получить именно то, что вы хотите, мы могли бы вместо этого сказать:

<xsl:apply-templates select="descendant::place[name]"/>

Разбиваем вещи на части

Мы используем оператор select для выбора.:) Мы не хотим применять шаблоны к всему , просто к конкретным вещам, и именно так мы указываем, какие вещи.

Мы используем descendant:: ось, потому что мы хотим перехватить все элементы <place>, содержащиеся в (то есть, потомке) элемента <root>.Если бы вместо этого мы сказали просто select="place[name]", это означало бы «применить шаблоны только к тем элементам <place>, которые являются прямыми потомками этого элемента <root>. Поскольку нет никаких элементов <place>, которые являются прямыми потомками этого *Элемент 1040 *, который не принесет вам ничего полезного.

Мы используем предикат [name], чтобы указать условие, которое нам нужно только для элементов <place>, которые имеют дочерние элементы <name>. Это позволяет намисключить <place attr="2" id="3">, который содержит еще <place>, но не имеет <name> собственных детей.

Надеюсь, это поможет. Пожалуйста, прокомментируйте, если есть что-то, что вы не понимаете.

Обновление: полный рабочий пример

Я неправильно определил, сколько деталей включить в свой предыдущий ответ.:)

Вот полный рабочий пример с аннотациями.

<?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="xs"
    version="1.0">

    <!-- Start at the logical root / since every XSLT *must* start there. -->
    <xsl:template match="/">
        <!-- We just pass everything along.  NOTE: We NEED to define templates
            for anything special we want to do, beyond just the default 
            XSLT behavior of outputting the string contents minus any elements. -->
        <xsl:apply-templates/>
    </xsl:template>

    <!-- We want to get the <root> element and attributes in our output, so
        we define a template to do that.-->
    <xsl:template match="root">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <!-- We also want to process the content of <root>, so we use 
                `xsl:apply-templates`.  Since we also want to be _selective_
                about what we process, we also specify a `select` statement
                with the XPath needed to identify what we want.  
                Again, we NEEd to define a template to process this,
                or we'll just get the text string content and none of 
                the elements.  -->
            <xsl:apply-templates select="descendant::place[name]"/>
        </xsl:copy>
    </xsl:template>

    <!-- Here we define what to do with the <place> elements that have
        <name> children.  Since your <place attr="2" id="3"> element
        has no <name> child, it gets omitted from the output. -->
    <xsl:template match="place[name]">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
        </xsl:copy>
    </xsl:template>

    <!-- Lastly, we define one more template that says "capture everything 
        else, and *don't* output anything".  This way, we don't get the text 
        string output of anything we haven't explicitly defined above. -->
    <xsl:template match="*"/>

</xsl:stylesheet>

Мы получаем только нужные элементы без содержащегося в них текста.

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