XSLT 1.0 не может выбирать переменные в xsl:value-of select
Я пытаюсь преобразовать кусок XML, который генерируется несколькими источниками. Если я ввожу текст непосредственно в оператор value-of select, он работает так, как задумано. Но как только я попытаюсь использовать переменные, это больше не будет работать.
Если я использую переменные в операторе xsl:for-each, он тоже работает нормально. Это XSL, который отлично работает
<xsl:variable name="crm_acc" select="account" />
<xsl:variable name="nav_acc" select="kontakt" />
<xsl:variable name="crm_fname" select="firstname" />
<xsl:variable name="nav_fname" select="fname" />
<TreeView>
<xsl:for-each select="$crm_acc | $nav_acc">
<TreeViewItem Header="Item">
<TreeViewItem Header="Firstname:" >
<xsl:value-of select="firstname | fname" />
</TreeViewItem>
</TreeViewItem>
</xsl:for-each>
</TreeView>
Но если я использую переменные внутри xsl:value-of select, он просто не будет работать так, как указано в следующем блоке кода. Я перепробовал множество комбинаций, например, используя только одну переменную с "firstname", "firstname | fname", пытаясь объединить (...) текст и т.д...
<TreeView>
<xsl:for-each select="$crm_acc | $nav_acc">
<TreeViewItem Header="Item">
<TreeViewItem Header="Firstname:" >
<xsl:value-of select="$crm_fname | $nav_fname" />
</TreeViewItem>
</TreeViewItem>
</xsl:for-each>
</TreeView>
Входной XML:
<?xml version='1.0'?>
<root>
<account system="CRM">
<firstname>test1</firstname>
</account>
<account system="CRM">
<firstname>test2</firstname>
</account>
<kontakt system="NAV">
<erstername>nav1</erstername>
</kontakt>
</root>
Только однажды я получил результат, так как я указал "account/firstname" в переменной. Но так я всегда получал значение первого элемента только в каждой итерации. Кажется, что он теряет контекст при указании переменных (что процессор в настоящее время находится внутри сущности "account" и должен выбрать "firstname" в качестве подузла).
Я читал, что что-то изменилось в операторе value-select с XSLT 1.0 до 2.0, но я не до конца понимаю, в чем разница (я довольно новичок в XSLT).
Как мне указать XSL, чтобы он работал?
Обновление: ожидаемый результат как в блоке кода 1
<?xml version="1.0" encoding="UTF-8"?>
<TabItem xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Header="Xaml Accounts Tab">
<TreeView>
<TreeViewItem Header="CRM">
<TreeViewItem Header="Firstname:">test1</TreeViewItem>
</TreeViewItem>
<TreeViewItem Header="CRM">
<TreeViewItem Header="Firstname:">test2</TreeViewItem>
</TreeViewItem>
<TreeViewItem Header="NAV">
<TreeViewItem Header="Firstname:">nav1</TreeViewItem>
</TreeViewItem>
</TreeView>
</TabItem>
Изменить: то, что я должен был упомянуть, это то, что эти шаблоны будут / могут быть изменены, так что было бы проще, если бы все "переменные" были в одном месте. Возможно, цель не называется "контакт", а что-то еще, и изменение ее во всем документе подвержено ошибкам и является избыточным. Также эти переменные используются в другом процессе, читаемом XmlReader. Я стараюсь сохранить как можно меньше проблем с конфигурацией и избыточностью.
3 ответа
Вот два возможных подхода, которые вы, возможно, захотите изучить: один использует переменную для хранения всех возможных имен элемента, к которому вы можете обратиться; другой предполагает, что положение элемента в его родительском элементе известно, поэтому имя не имеет значения.
В следующей таблице стилей показаны оба метода: первый для родительского элемента, второй для дочернего имени.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:variable name="CRM">
<name>account</name>
<name>kontakt</name>
</xsl:variable>
<xsl:template match="/">
<TreeView>
<xsl:for-each select="root/*[local-name()=exsl:node-set($CRM)/name]">
<TreeViewItem Header="{@system}">
<TreeViewItem Header="Firstname:" >
<xsl:value-of select="*[1]" />
</TreeViewItem>
</TreeViewItem>
</xsl:for-each>
</TreeView>
</xsl:template>
</xsl:stylesheet>
В XSLT вы должны забыть о любых процедурных парадигмах, которые вы знаете. Вам следует избегать xsl:for-each
всякий раз, когда неуместно и вместо этого пишите отдельные шаблоны
Точно так же нет необходимости использовать переменные в вашем случае. То есть, их нельзя использовать, но, как отмечает @helderdarocha, вы должны знать об их неизменности и контексте.
В таблице стилей, которую вы показываете, вероятно, что большинство переменных пустые, потому что выбранные вами элементы не существуют в этом контексте. Чтобы выбрать элементы независимо от того, где они находятся в иерархии, начните выражение с //
,
стилей
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/root">
<TabItem xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Header="Xaml Accounts Tab">
<TreeView>
<xsl:apply-templates/>
</TreeView>
</TabItem>
</xsl:template>
<xsl:template match="account|kontakt">
<TreeViewItem Header="{@system}">
<xsl:apply-templates/>
</TreeViewItem>
</xsl:template>
<xsl:template match="firstname|erstername">
<TreeViewItem Header="Firstname:">
<xsl:value-of select="."/>
</TreeViewItem>
</xsl:template>
</xsl:stylesheet>
Выход
<?xml version="1.0" encoding="UTF-8"?>
<TabItem xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Header="Xaml Accounts Tab">
<TreeView>
<TreeViewItem xmlns="" Header="CRM">
<TreeViewItem Header="Firstname:">test1</TreeViewItem>
</TreeViewItem>
<TreeViewItem xmlns="" Header="CRM">
<TreeViewItem Header="Firstname:">test2</TreeViewItem>
</TreeViewItem>
<TreeViewItem xmlns="" Header="NAV">
<TreeViewItem Header="Firstname:">nav1</TreeViewItem>
</TreeViewItem>
</TreeView>
</TabItem>
Проблема в том, что переменные в XSLT неизменны. Относитесь к ним как к постоянным. Когда ты сделал
<xsl:variable name="crm_fname" select="firstname" />
Вы выбрали "имя" в текущем контексте. Я полагаю, что код в <xsl:template match='root'>
контекст, поэтому вы выбрали "корень / имя". Нет такого узла, поэтому результат пустой, как и ожидалось.
Когда вы вызываете переменную в for-each, вы просто печатаете содержимое переменной, что является ничем.
Это сработало, когда вы сказали, что переменная account/firstname
поскольку этот узел существует, но поскольку переменная фактически является константой, она содержит содержимое первого совпадения, и любые другие попытки изменить это значение были проигнорированы.