XSLT - как получить значение имени узла?
У нас есть (возможно, необычно отформатированные) файлы XML, которые (очень упрощенно) выглядят так:
<Contacts>
<Contact>
<Private>
<Name>John Doe</Name>
<Address>Somewhere1</Address>
...
</Private>
</Contact>
<Contact>
<Business>
<Name>John Business</Name>
<Address>Somewhere2</Address>
<BusinessAddress>Somewhere3</BusinessAddress>
...
</Business>
</Contact>
...
</Contacts>
На самом деле, у нас есть несколько десятков различных типов "контактных" узлов со многими другими атрибутами, некоторые из которых, однако, являются общими для всех типов...
В настоящее время мы используем таблицу стилей для форматирования файла XML, который повторяет форматирование для всех общих атрибутов для каждого типа узла "Контакт":
<!-- ... loads of prologue code -->
<xsl:for-each select="Contacts/Contact/.">
<xsl:choose>
<xsl:when test="Private">
<!-- code to format each and every attribute of the current
"Contact" element, e.g.: -->
<td class="header">Private Contact</td>
<td class="detail"><xsl:value-of select="./Name"/></td>
<!-- ... -->
</xsl:when>
<xsl:when test="Business">
<!-- code to format each and every attribute of the current
"Contact" element, e.g.: -->
<td class="header">Business Contact/<td>
<td class="detail"><xsl:value-of select="./Name"/></td>
<!-- ... -->
</xsl:when>
<!-- ... -->
</xsl:choose>
</xsl:for-each>
Но мы получаем все больше и больше <Contact>
типы с большим и большим количеством атрибутов, в результате чего получается таблица стилей длиной в милю, поэтому я хочу упростить таблицу стилей следующим образом:
<!-- ... loads of prologue code -->
<xsl:for-each select="Contacts/Contact/.">
<!-- loads of formatting stuff common to each "Contact" type, but with
some wording that's "Contact" type dependant, e.g. -->
<td class="header">**????** Contact</td>
<td class="detail"><xsl:value-of select="./Name"/></td>
<xsl:choose>
<xsl:when **????** = "Private">
<xsl:variable name="contactWhen">off</xsl:variable>
</xsl:when>
<xsl:when **????** = "Business">
<xsl:variable name="contactWhen">office</xsl:variable>
</xsl:when>
<!-- ... -->
</xsl:choose>
<td class="header">Contact/<td>
<td class="detail">Only during $contactWhen hours</td>
<xsl:choose>
<xsl:when test="Private">
<!-- code to format attributes specific for the current "Contact" -->
</xsl:when>
<xsl:when test="Business">
<!-- code to format attributes specific for the current "Contact" -->
</xsl:when>
<!-- ... -->
</xsl:choose>
</xsl:for-each>
Мои извинения, если в приведенных выше примерах кода есть какие-либо опечатки или синтаксические ошибки, но я не очень близок к синтаксису XSLT, и большая часть приведенного выше кода была создана вручную для примера...
Моя проблема в том, что я не могу получить значение имени узла под Contact
в этом примере Private
или же Business
, Я пытался использовать все варианты, которые я мог придумать, оба value-of select=
и просто select= (a.o. "", ".", "./."
, Contacts/Contact
, Contacts/Contact/.
, [local-]name()
и даже Field[@name='.']
).
Некоторые попытки приводят к ошибкам, некоторые приводят к пустой строке, другие возвращают имя родительского узла, т.е. Contact
, а некоторые возвращают значения всех подчиненных атрибутов (но без имен атрибутов) в виде одной строки...:-(
Что я должен код для **????**
проверить значение имени текущего узла и, в конечном итоге, присвоить некоторой переменной (переменным) значение, основанное на результатах этого теста?
Спасибо за любую помощь,
Юуль
Привет,
Спасибо за ценный вклад, и я определенно рассмотрю вопрос о преобразовании этой и некоторых других аналогичных таблиц стилей в шаблоны, но сейчас я вынужден выполнить этот проект.
Я попробовал все 3 предложения в цикле "для каждого" и непосредственно перед "выбрать", но все 3 не сработали:
<td class="content2" colspan="2"><xsl:value-of select="ancestor::Record/child::*[1]name"/></td>
не удается с ошибкой XML: ожидаемое "EOF" найдено "name"
<td class="content2" colspan="2"><xsl:value-of select="name()"/></td>
возвращает "Контакт", а не имя первого дочернего имени
<xsl:variable name="contactType">
<xsl:choose>
<xsl:when test="Private">Private</xsl:when>
<xsl:when test="Business">Business</xsl:when>
</xsl:choose>
<td class="content2" colspan="2"><xsl:value-of select="$contactType"/></td>
</xsl:variable>
не удается с ошибкой XML: ссылка на переменную contactType не может быть разрешена. Переменная может не быть определена или не может находиться в области видимости.
Может ли это быть вызвано наличием этих конструкций во встроенном коде, а не в шаблоне.
Пожалуйста, порекомендуйте,
Юуль
5 ответов
Использование xsl: выбор для включения имен элементов - неприятный запах кода в XSLT: для этого и нужны правила шаблонов. Моим первым шагом к улучшению этого кода будет замена кода
<xsl:for-each select="Contacts/Contact/.">
<xsl:choose>
<xsl:when test="Private">
...
<xsl:when test="Business">
с
<xsl:apply-templates select="Contacts/Contact"/>
а также
<xsl:template match="Contact[Private]">
...
</xsl:template>
<xsl:template match="Contact[Business]">
...
</xsl:template>
и т.п.
Это не сразу решит проблему повторного использования кода на следующем уровне ниже. Это происходит, когда вы начинаете делать apply-templates на этом уровне.
<xsl:template match="Contact[Private]">
<td class="header">Private Contact</td>
<xsl:apply-templates select="Private/*"/>
</xsl:template>
<xsl:template match="Name | Address">
<td class="detail"><xsl:value-of select="."/></td>
</xsl:template>
Шаблонные правила - это способ, которым XSLT был разработан для использования. Этот пример является отличной иллюстрацией преимуществ, которые вы получаете, используя их правильно.
Измените вашу переменную на:
<xsl:variable name="contactWhen">
<xsl:choose>
<xsl:when test="Private">off</xsl:when>
<xsl:when test="Business">office</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:for-each select="Contacts/Contact">
<xsl:for-each select="child::*[1]">
<td class="header"><xsl:value-of select="name()"/><xsl:text> Contact</xsl:text></td>
<td class="detail"><xsl:value-of select="ancestor::Contact/child::*[1]/Name"/></td>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
Try It
Имя узла первого потомка <Contact>
можно получить с помощью name()
функция. Однако, поскольку существуют условия для проверки имени узла, а затем сделать что-то еще, а именно. заполнить другое значение в $contactWhen
или форматирование атрибутов в зависимости от типа контакта, вам придется использовать <xsl:choose><xsl:when test=""></xsl:when></xsl:choose>
условия для каждого уникального контакта.
Существуют различные подходы для достижения желаемого результата, и ниже приведен шаблонный подход. Шаблон ниже соответствует первому потомку <Contact>
и обрабатывает данные.
<xsl:template match="Contact/*[1]">
<!-- fetching node name -->
<xsl:variable name="nodeName" select="name()" />
<td class="header"><xsl:value-of select="concat($nodeName, ' Contact')" /></td>
<td class="detail"><xsl:value-of select="Name" /></td>
<!-- contact hours -->
<xsl:variable name="contactWhen">
<xsl:choose>
<xsl:when test="$nodeName = 'Private'">off</xsl:when>
<xsl:when test="$nodeName = 'Business'">office</xsl:when>
</xsl:choose>
</xsl:variable>
<td class="header">Contact</td>
<td class="detail"><xsl:value-of select="concat('Only during ', $contactWhen, ' hours')" /></td>
<!-- adding contact specific nodes and attributes -->
<xsl:choose>
<xsl:when test="$nodeName = 'Private'">
<!-- code to format attributes specific for the "Private" Contact -->
<td class="{$nodeName}">This is a Private Contact</td>
</xsl:when>
<xsl:when test="$nodeName = 'Business'">
<!-- code to format attributes specific for the "Business" Contact -->
<td class="{$nodeName}">This is a Business Contact</td>
</xsl:when>
</xsl:choose>
</xsl:template>
Вывод будет выглядеть примерно так
<td class="header">Private Contact</td>
<td class="detail">John Doe</td>
<td class="header">Contact</td>
<td class="detail">Only during off hours</td>
<td class="Private">This is a Private Contact</td>
<td class="header">Business Contact</td>
<td class="detail">John Business</td>
<td class="header">Contact</td>
<td class="detail">Only during office hours</td>
<td class="Business">This is a Business Contact</td>
Для улучшения обслуживания кода <xsl:choose>
фрагменты кода могут быть перемещены в различные шаблоны, а затем с помощью <xsl:call-template>
они могут быть вызваны для обработки. Это будет модульным кодом и сделает его менее загроможденным.
Я обнаружил проблему... Ответ Джоэла от 2-х дней назад действительно помогает; Я просто имел ссылку на переменную в самом объявлении, в то время как ссылка (и) должна быть сделана ПОСЛЕ полного объявления следующим образом:
<xsl:variable name="contactType">
<xsl:choose>
<xsl:when test="Private">Private</xsl:when>
<xsl:when test="Business">Business</xsl:when>
</xsl:choose>
</xsl:variable>
<td class="content2" colspan="2"><xsl:value-of select="$contactType"/></td>
на самом деле добавляет "Частный" или "Бизнес" в форматированный XML:-)
Спасибо всем, кто внес свой вклад, также пытаясь улучшить мои ограниченные навыки XSLT,
Юуль