Если в узле несколько значений, объедините их в одну строку
Я новичок в программировании xslt и мне нужно решение проблемы. Я хочу преобразовать XML-файл в текстовый файл CSV. Я импортирую этот CSV в лист Excel.
Во входном XML-файле, если в узле несколько значений, объедините их в одну строку.
Входной XML-файл, как показано ниже.
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="ForumCsv.xsl"?>
<Inventory>
<Line>
<LineNumber>line</LineNumber>
<Description>desc</Description>
<Matrix>quan</Matrix>
<Matrix>quan1</Matrix> <!-- added -->
<Date>date</Date>
</Line>
<Line>
<LineNumber>1</LineNumber>
<Description>Oak chairs</Description>
<Matrix>5</Matrix>
<Matrix>20</Matrix> <!-- added -->
<Matrix>16</Matrix> <!-- added -->
<Date>31 Dec 2004</Date>
</Line>
<Line>
<LineNumber>2</LineNumber>
<Description>Dining tables</Description>
<Matrix>
<SubComp>100</SubComp>
<SubComp>300</SubComp>
</Matrix>
<Date>31 Dec 2004</Date>
</Line>
<Line>
<LineNumber>3</LineNumber>
<Description>Folding chairs</Description>
<Matrix>4</Matrix>
<Date>29 Dec 2004</Date>
</Line>
<Line>
<LineNumber>4</LineNumber>
<Description>Couch</Description>
<Matrix>1</Matrix>
<Date>31 Dec 2004</Date>
</Line>
</Inventory>
Ожидаемый результат как ниже.
line|desc|quan,quan1|date
1|Oak chairs|5,20,16| Dec 2004
2|Dining tables|100,300|31 Dec 2004
3|Folding chairs|4|29 Dec 2004
Исходный код, который я написал, приведен ниже.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="yes" encoding="ISO-8859-1" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template name="Newline"><xsl:text>
</xsl:text></xsl:template>
<xsl:template match="Inventory">
<xsl:apply-templates select="Line"/>
</xsl:template>
<xsl:template match="Line">
<xsl:for-each select="*">
<!-- THIS IS WHERE I need help. I aim to put a test condition where I wish to identify sibling nodes .
If sibling nodes are found then dont use '|', but use ';'
Also I want to paramterize the delimiter
<xsl:test ????? >
<xsl:value-of select="concat(substring(';',1,position()-1),.)"/>
</xsl:template>
-->
<xsl:value-of select="."/>
<xsl:if test="position() != last()">
<xsl:value-of select="'|'"/>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
Разделителями являются "|" а также ",". Но я бы хотел их параматизировать.
Также код должен быть универсальным. Если добавлено более одного элемента, вывод должен быть таким же, то есть "|" или "," с разделителями. Нет жесткого кодирования узлов
3 ответа
Это полное, короткое и простое (push-стиль, без явных условных инструкций) решение XSLT 1.0:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pSubItemSeparator" select="','"/>
<xsl:param name="pItemSeparator" select="'|'"/>
<xsl:template match="Line">
<xsl:apply-templates/>
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="Line/*[1]">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match=
"Line/*[position() >1]
|
Line/*/*[1]
">
<xsl:value-of select="concat($pItemSeparator, .)"/>
</xsl:template>
<xsl:template priority="2" match=
"Line/*[name()
=
name(preceding-sibling::*[1])]
|
Line/*/*[position() > 1]
">
<xsl:value-of select="concat($pSubItemSeparator, .)"/>
</xsl:template>
<xsl:template match="Line/*[*]">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
при применении к предоставленному документу XML:
<Inventory>
<Line>
<LineNumber>line</LineNumber>
<Description>desc</Description>
<Matrix>quan</Matrix>
<Matrix>quan1</Matrix>
<!-- added -->
<Date>date</Date>
</Line>
<Line>
<LineNumber>1</LineNumber>
<Description>Oak chairs</Description>
<Matrix>5</Matrix>
<Matrix>20</Matrix>
<!-- added -->
<Matrix>16</Matrix>
<!-- added -->
<Date>31 Dec 2004</Date>
</Line>
<Line>
<LineNumber>2</LineNumber>
<Description>Dining tables</Description>
<Matrix>
<SubComp>100</SubComp>
<SubComp>300</SubComp>
</Matrix>
<Date>31 Dec 2004</Date>
</Line>
<Line>
<LineNumber>3</LineNumber>
<Description>Folding chairs</Description>
<Matrix>4</Matrix>
<Date>29 Dec 2004</Date>
</Line>
<Line>
<LineNumber>4</LineNumber>
<Description>Couch</Description>
<Matrix>1</Matrix>
<Date>31 Dec 2004</Date>
</Line>
</Inventory>
желаемый, правильный результат получается:
line|desc|quan,quan1|date
1|Oak chairs|5,20,16|31 Dec 2004
2|Dining tables|100,300|31 Dec 2004
3|Folding chairs|4|29 Dec 2004
4|Couch|1|31 Dec 2004
Вот решение XSLT 2.0, которое вы можете использовать с процессорами XSLT 2.0, такими как Saxon 9 или AltovaXML или XQSharp:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:param name="lf" select="' '"/>
<xsl:param name="is" select="','"/>
<xsl:param name="cs" select="'|'"/>
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*/*">
<xsl:if test="position() gt 1">
<xsl:value-of select="$lf"/>
</xsl:if>
<xsl:for-each-group select="*" group-adjacent="node-name(.)">
<xsl:if test="position() gt 1">
<xsl:value-of select="$cs"/>
</xsl:if>
<xsl:value-of select="current-group()/descendant-or-self::*[not(*)]" separator="{$is}"/>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
Попробуй это:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="yes" encoding="ISO-8859-1" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="csvsep" select="'|'"/>
<xsl:param name="datasep" select="','"/>
<!-- Default -->
<xsl:template match="*|@*">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template name="Newline"><xsl:text>
</xsl:text></xsl:template>
<xsl:template match="Line/*">
<xsl:value-of select="concat(., $csvsep)"/>
</xsl:template>
<xsl:template match="Line/*[./*]">
<xsl:apply-templates select="./*"/>
<xsl:value-of select="$csvsep"/>
</xsl:template>
<xsl:template match="Line/*[position()=last()]">
<xsl:value-of select="."/>
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="Line/*[position()=last() and ./*]">
<xsl:apply-templates select="./*"/>
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="Line/*[name(./following-sibling::*)=name(.)]">
<xsl:value-of select="concat(., $datasep)"/>
</xsl:template>
<xsl:template match="Line/*/*">
<xsl:value-of select="concat(., $datasep)"/>
</xsl:template>
<xsl:template match="Line/*/*[position()=last()]">
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
Вы можете указать разделители в качестве параметров таблицы стилей, для них по умолчанию заданы значения по умолчанию.
в основном все шаблоны соответствуют детям или внукам Line
элемент. Предполагается, что внуки братьев и сестер являются частью одного поля CSV. Последний элемент на любом уровне обрабатывается специально. обратите внимание, что порядок шаблонов является значительным.
Это решение работает с процессорами xslt 1.0.
надеюсь, это поможет, Карстен