XSLT: выбрать элемент по значению переменной

У меня есть некоторые XML-структуры, как показано ниже:

<report>
<columns>
<column col_id="col_1">Column 1 title</column>
<column col_id="col_2">Column 2 title</column>
<column col_id="col_3">Column 3 title</column>
</columns>
<rows>
<row>
<col_1>Value 1</col_1>
<!-- No col_2 value exists -->
<col_3>value 3</col_3>
</row>
<row>
<col_1>Value 1</col_1>
<col_2>Value 2</col_2>
<col_3>value 3</col_3>
<col_4>Value 4</col_4><!-- Not interested in this value -->
</row>
</rows>
</report>

Я хочу вывести эти данные в виде таблицы HTML, используя XSLT.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="utf-8" indent="yes" />
<xsl:template match="Report">
<!-- Get the column names -->
<xsl:variable name="arr_columns" select="./columns/*" />

<div id="example_report" class="report">
<table style="width:100%">
<thead>
<tr class="header">
<!-- Columns first -->
<xsl:for-each select="$arr_columns">
<th><xsl:value-of select="." /></th>
</xsl:for-each>
</tr>
</thead>
<tbody>
<!-- Now the actual data -->
<xsl:for-each select="./rows/row">
<xsl:variable name="row" select="." />
<tr>
<xsl:for-each select="$arr_columns">
<xsl:variable name="col_id" select="./@column_id" />
<td><xsl:value-of select="$row/$col_id" /></td>
</xsl:for-each>
</tr>
</xsl:for-each>
</tbody>
</table>
</div>
</xsl:template>
</xsl:stylesheet>

Я надеялся что <xsl:value-of select="$row/$col_id" /> позволил бы мне выбрать столбец на основе его имени, хранящегося в переменной col_id. Тем не менее, PHP XSLTProcessor взорвется, и отчет не будет отображен.

Предупреждение PHP: XSLTProcessor :: transformToXml (): Нет таблицы стилей, связанной с этим объектом

Я попытался вывести только $ row (который, как и ожидалось, выводит всю строку в виде строки), и $ row / col_1 (который выводит содержимое col_1, как и ожидалось), но пытался получить доступ к узлу на основе значения переменной просто не похоже на работу. Если я выведу значение $ col_id, он отобразит имя узла, который я хочу вывести, так что переменная будет правильно установлена ​​в цикле.

Я предполагаю, что это возможно, и я делаю это неправильно, но мне трудно найти пример того, как сделать это правильно. Я нашел примеры, которые позволяют вам выбирать узлы на основе атрибутов, но, как вы можете видеть, у меня есть узлы, у которых нет атрибутов, их имя указывает, что они содержат.

Может кто-нибудь помочь с написанием правильного селектора для этого случая?

Для полноты, это вывод, который я надеюсь сгенерировать:

<table>
<thead>
<tr>
<th>Column 1 title</th>
<th>Column 2 title</th>
<th>Column 3 title</th>
</tr>
</thead>
<tbody>
<tr>
<td>Value 1</td>
<td></td>
<td>value 3</td>
</tr>
<tr>
<td>Value 1</td>
<td>value 2</td>
<td>value 3</td>
</tr>
</tbody>
</table>

1

Решение

Попробуйте это так?

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="/report">
<xsl:variable name="columns" select="columns/column" />
<table style="width:100%">
<thead>
<tr>
<xsl:for-each select="$columns">
<th><xsl:value-of select="." /></th>
</xsl:for-each>
</tr>
</thead>
<tbody>
<xsl:for-each select="rows/row">
<xsl:variable name="current-row" select="." />
<tr>
<xsl:for-each select="$columns">
<td><xsl:value-of select="$current-row/*[local-name()=current()/@col_id]"/></td>
</xsl:for-each>
</tr>
</xsl:for-each>
</tbody>
</table>
</xsl:template>

</xsl:stylesheet>
2

Другие решения

Тем более, что у вас есть такое четкое соответствие между элементами входного XML-кода и элементами целевого HTML-кода, рассмотрите более ориентированный на сопоставление декларативный подход, а не текущий процедурный подход на основе цикла:

Декларативный подход

[Обновлено в соответствии с замечанием, сделанным @ michael.hor257k, о некотором процедурном цикле после сопоставления row помогает удовлетворить требование о том, что опущено col_ ячейка должна привести к пустому td когда связанный column существует.]
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="utf-8" indent="yes" />

<xsl:template match="report">
<div id="example_report" class="report">
<table style="width:100%">
<xsl:apply-templates/>
</table>
</div>
</xsl:template>

<xsl:template match="row">
<tr>
<xsl:variable name="row" select="."/>
<xsl:for-each select="../../columns/column">
<xsl:variable name="col_id" select="@col_id"/>
<td>
<xsl:value-of select="$row/*[local-name()=$col_id]"/>
</td>
</xsl:for-each>
</tr>
</xsl:template>

<xsl:template match="columns">
<thead><xsl:apply-templates/></thead>
</xsl:template>

<xsl:template match="column">
<th><xsl:apply-templates/></th>
</xsl:template>

<xsl:template match="rows">
<tbody><xsl:apply-templates/></tbody>
</xsl:template>
</xsl:stylesheet>
1

По вопросам рекламы [email protected]