Вопрос
Должен существовать универсальный способ преобразования некоторого иерархического XML, например:
<element1 A="AValue" B="BValue">
<element2 C="DValue" D="CValue">
<element3 E="EValue1" F="FValue1"/>
<element3 E="EValue2" F="FValue2"/>
</element2>
...
</element1>
в плоский XML (html), попутно собирая выбранные атрибуты и предоставляя разные метки для атрибутов, которые становятся заголовками столбцов.
<table>
<tr>
<th>A_Label</th>
<th>D_Label</th>
<th>E_Label</th>
<th>F_Label</th>
</tr>
<tr>
<td>AValue</td>
<td>DValue</td>
<td>EValue1</td>
<td>FValue1</td>
</tr>
<tr>
<td>AValue</td>
<td>DValue</td>
<td>EValue2</td>
<td>FValue2</td>
</tr>
<table>
Хорошо, общего решения нет из-за перемаркировки атрибутов, но, надеюсь, вы поняли, что я имею в виду.Я только начал изучать XSLT/XPATH, поэтому разберусь с этим в свое время, но любые подсказки будут полезны.
Решение
Я не уверен на 100% в том, что вы пытаетесь сделать, но это решение может сработать, если ваши element1, element2 и element3 последовательно вложены.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<table>
<xsl:apply-templates select="//element3"></xsl:apply-templates>
</table>
</xsl:template>
<xsl:template match="element3">
<tr>
<td><xsl:value-of select="../../@A"/></td>
<td><xsl:value-of select="../../@B"/></td>
<td><xsl:value-of select="../@C"/></td>
<td><xsl:value-of select="../@D"/></td>
<td><xsl:value-of select="@E"/></td>
<td><xsl:value-of select="@F"/></td>
</tr>
<xsl:apply-templates select="*"></xsl:apply-templates>
</xsl:template>
</xsl:stylesheet>
Другие советы
Мне нужен был аналогичный XSLT, но с неизвестной глубиной, вот как я это сделал.
Сначала добавьте оболочку для полученной HTML-таблицы/списка определений и вызовите шаблон mode="puke", чтобы сгладить наше XML-дерево:
<xsl:element name="div">
<xsl:attribute name="class" select="puke" />
<xsl:apply-templates select="$notice" mode="puke" />
</xsl:element>
Здесь мы сопоставляем каждый узел для отображения его содержимого (например.text()) и его атрибуты.Мы делаем это рекурсивно.Я использовал dl/dt/dd, потому что мое исходное дерево было сложным деревом, которое нельзя было свести в формат .
<!-- @description:
Display all field from the notice so the customer can tell what he want
-->
<xsl:template match="node()" mode="puke">
<xsl:message>
puking : <xsl:value-of select="local-name( . )" />
</xsl:message>
<xsl:element name="dl">
<xsl:element name="dt">
<xsl:attribute name="class">tagName</xsl:attribute>
<xsl:value-of select="local-name( . )" />
</xsl:element>
<xsl:element name="dd">
<xsl:attribute name="class">tagText</xsl:attribute>
<xsl:value-of select="text()" /></xsl:element>
<xsl:element name="dl">
<xsl:attribute name="class">attr</xsl:attribute>
<!-- display attribute -->
<xsl:apply-templates select="@*" />
</xsl:element>
</xsl:element>
<!-- recursive call on node() -->
<xsl:apply-templates select="./*" mode="puke" />
</xsl:template>
Сопоставьте атрибут данного узла и отобразите его.
Использование CSS для форматирования полученного HTML:
<style>
.puke {
background-color: #BDD6DE;
clear: both;
}
.tagName, .attrName {
float: left;
}
.tagText, .attrText {
clear: right;
}
</style>
У нас уже есть программа Pro*C, читающая из базы данных Oracle, она вызывает сценарий Perl, который, в свою очередь, выполняет некоторую Java для извлечения данных в формате XML из вышеупомянутой базы данных для вызова пакетного файла для выполнения некоторого vbscript, пересылающего файл по FTP в какой-либо другой сервер.Я очень надеялся на что-нибудь на Фортране.
Необходимо уточнить исходный вопрос:
- Что происходит с BValue и CValue в исходном вопросе?Есть ли причина, по которой они не должны быть частью плоской структуры?
- Все ли элементы XML-документа имеют два атрибута или это совершенно произвольно?
- Есть ли только 3 типа элементов и всегда ли они вложены, как показано в примере?
- Может ли ваш элемент1 повторяться или это корневой элемент вашего документа?
В XSLT можно писать очень общие преобразователи, но часто гораздо проще написать таблицу стилей для преобразования документа, если можно принять во внимание любые известные ограничения.
Я использовал расширенную версию приведенного ниже шаблона для выравнивания структурированного XML.Предупреждение:В исходной версии был некоторый код, специфичный для конкретного случая (он фактически превращал XML в CSV), который я только что удалил и не тестировал эту версию.
Основной принцип работы должен быть ясен:он печатает все, что не имеет дочерних узлов, и в противном случае рекурсивно вызывает шаблон в узле(), у которого есть дочерние элементы.Я не думаю, что он обрабатывает атрибуты и комментарии правильно, как сейчас, но это не должно быть сложно исправить.
<?xml version="1.0" encoding="UTF-8"?>
<!-- XSL template to flatten structured XML, before converting to CSV. -->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" encoding="UTF-8"/>
<xsl:strip-space elements="*" />
<xsl:template match="/">
<xsl:apply-templates select="//yourElementsToFlatten"/>
</xsl:template>
<xsl:template match="//yourElementsToFlatten">
<xsl:apply-templates select="@*|node()"/>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:choose>
<!-- If the element has multiple childs, call this template
on its children to flatten it-->
<xsl:when test="count(child::*) > 0">
<xsl:apply-templates select="@*|node()"/>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:value-of select="text()" />
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>