Ключом является ваше заявление 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>
Мы получаем только нужные элементы без содержащегося в них текста.