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

Другие вопросы по тегам