Как преобразовать атрибуты в теги с помощью XSLT и избежать дублирования?
Прежде всего, я провожу здесь много времени и часто получаю помощь в ответах на уже здесь. Но теперь я сталкиваюсь с новой темой для меня - XSLT - и мне действительно нужна помощь сейчас.
Хорошо, проблема у меня заключается в том, чтобы преобразовать XML-файл, полученный из Excel, для повторного использования в форме XFA. Excel поддерживает только плоские данные, но это не соответствует иерархии дат в моей форме XFA.
Вот данные, которые я получаю из Excel:
<Accounts>
<Account Category="001" Region="AAA" Name="dolor" Value="123" Type="A" Rating="1,25"/>
<Account Category="001" Region="AAA" Name="sit amet" Value="134" Type="A" Rating="1,25"/>
<Account Category="004" Region="BBB" Name="consetetur" Value="434" Type="A" Rating="1,25"/>
<Account Category="002" Region="AAA" Name="sadipscing" Value="84" Type="A" Rating="1,25"/>
<Account Category="007" Region="ZZZ" Name="elitr" Value="33" Type="A" Rating="1,25"/>
<Account Category="004" Region="CCC" Name="aliquyam" Value="6" Type="A" Rating="1,25"/>
<Account Category="001" Region="BBB" Name="ipsum" Value="34" Type="A" Rating="1,25"/>
<Account Category="003" Region="ZZZ" Name="lorem" Value="75" Type="A" Rating="2.87"/>
</Accounts>
И вот как я хочу преобразовать это:
<Accounts>
<Category name="001">
<Region name="AAA">
<Account Name="dolor" Value="123" Type="A" Rating="1,25"/>
<Account Name="sit amet" Value="134" Type="A" Rating="1,25"/>
</Region>
<Region name="BBB">
<Account Name="ipsum" Value="34" Type="A" Rating="1,25"/>
</Region>
</Category>
<Category name="002">
<Region name="BBB">
<Account Name="sadipscing" Value="84" Type="A" Rating="1,25"/>
</Region>
</Category>
<Category name="003">
<Region name="ZZZ">
<Account Name="lorem" Value="75" Type="A" Rating="2.87"/>
</Region>
</Category>
...
</Accounts>
Я уже потратил пару дней, но единственное, чего я достиг, - это создать новые теги для категории без дубликатов. Вот мой текущий файл XSLT.
<?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" />
<xsl:variable name="Root" select="Accounts" />
<xsl:variable name="CategoryList" select="$Root/Account[not(@Category=following::Account/@Category)]" />
<xsl:template match="/">
<Accounts>
<xsl:for-each select="$CategoryList">
<xsl:variable name="varCategory" select="./@Category" />
<Category>
<xsl:value-of select="$varCategory"/>
</Category>
</xsl:for-each>
</Accounts>
</xsl:template>
</xsl:stylesheet>
Любая помощь приветствуется.
1 ответ
Вы не говорите, используете ли вы XSLT 1.0 или XSLT 2.0.
Это многоуровневая сортировка, и я говорю, что для XSLT 1.0 моим студентам легче писать с использованием метода группирования на основе переменных.
Для XSLT 2.0 это очень легко сделать с помощью доступных конструкций.
Оба решения здесь:
T:\ftemp>type mxps.xml
<Accounts>
<Account Category="001" Region="AAA" Name="dolor" Value="123" Type="A" Rating="1,25"/>
<Account Category="001" Region="AAA" Name="sit amet" Value="134" Type="A" Rating="1,25"/>
<Account Category="004" Region="BBB" Name="consetetur" Value="434" Type="A" Rating="1,25"/>
<Account Category="002" Region="AAA" Name="sadipscing" Value="84" Type="A" Rating="1,25"/>
<Account Category="007" Region="ZZZ" Name="elitr" Value="33" Type="A" Rating="1,25"/>
<Account Category="004" Region="CCC" Name="aliquyam" Value="6" Type="A" Rating="1,25"/>
<Account Category="001" Region="BBB" Name="ipsum" Value="34" Type="A" Rating="1,25"/>
<Account Category="003" Region="ZZZ" Name="lorem" Value="75" Type="A" Rating="2.87"/>
</Accounts>
T:\ftemp>call xslt mxps.xml mxps.xsl
<?xml version="1.0" encoding="utf-8"?>
<Accounts>
<Category name="001">
<Region name="AAA">
<Account Name="dolor" Value="123" Type="A" Rating="1,25"/>
<Account Name="sit amet" Value="134" Type="A" Rating="1,25"/>
</Region>
<Region name="BBB">
<Account Name="ipsum" Value="34" Type="A" Rating="1,25"/>
</Region>
</Category>
<Category name="002">
<Region name="AAA">
<Account Name="sadipscing" Value="84" Type="A" Rating="1,25"/>
</Region>
</Category>
<Category name="003">
<Region name="ZZZ">
<Account Name="lorem" Value="75" Type="A" Rating="2.87"/>
</Region>
</Category>
<Category name="004">
<Region name="BBB">
<Account Name="consetetur" Value="434" Type="A" Rating="1,25"/>
</Region>
<Region name="CCC">
<Account Name="aliquyam" Value="6" Type="A" Rating="1,25"/>
</Region>
</Category>
<Category name="007">
<Region name="ZZZ">
<Account Name="elitr" Value="33" Type="A" Rating="1,25"/>
</Region>
</Category>
</Accounts>
T:\ftemp>type mxps.xsl
<?xml version="1.0" encoding="US-ASCII"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes"/>
<xsl:template match="Accounts">
<Accounts>
<xsl:variable name="accounts" select="Account"/>
<xsl:for-each select="$accounts">
<xsl:sort select="@Category"/>
<xsl:if test="generate-id(.)=
generate-id($accounts[@Category=current()/@Category][1])">
<Category name="{@Category}">
<xsl:variable name="categories"
select="$accounts[@Category=current()/@Category]"/>
<xsl:for-each select="$categories">
<xsl:sort select="@Region"/>
<xsl:if test="generate-id(.)=
generate-id($categories[@Region=current()/@Region][1])">
<Region name="{@Region}">
<xsl:for-each select="$categories[@Region=current()/@Region]">
<xsl:sort select="@Name"/>
<Account>
<xsl:copy-of select="@Name"/>
<xsl:copy-of select="@Value"/>
<xsl:copy-of select="@Type"/>
<xsl:copy-of select="@Rating"/>
</Account>
</xsl:for-each>
</Region>
</xsl:if>
</xsl:for-each>
</Category>
</xsl:if>
</xsl:for-each>
</Accounts>
</xsl:template>
</xsl:stylesheet>
T:\ftemp>call xslt2 mxps.xml mxps2.xsl
<?xml version="1.0" encoding="UTF-8"?>
<Accounts>
<Category name="001">
<Region name="AAA">
<Account Name="dolor" Value="123" Type="A" Rating="1,25"/>
<Account Name="sit amet" Value="134" Type="A" Rating="1,25"/>
</Region>
<Region name="BBB">
<Account Name="ipsum" Value="34" Type="A" Rating="1,25"/>
</Region>
</Category>
<Category name="002">
<Region name="AAA">
<Account Name="sadipscing" Value="84" Type="A" Rating="1,25"/>
</Region>
</Category>
<Category name="003">
<Region name="ZZZ">
<Account Name="lorem" Value="75" Type="A" Rating="2.87"/>
</Region>
</Category>
<Category name="004">
<Region name="BBB">
<Account Name="consetetur" Value="434" Type="A" Rating="1,25"/>
</Region>
<Region name="CCC">
<Account Name="aliquyam" Value="6" Type="A" Rating="1,25"/>
</Region>
</Category>
<Category name="007">
<Region name="ZZZ">
<Account Name="elitr" Value="33" Type="A" Rating="1,25"/>
</Region>
</Category>
</Accounts>
T:\ftemp>type mxps2.xsl
<?xml version="1.0" encoding="US-ASCII"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output indent="yes"/>
<xsl:template match="Accounts">
<Accounts>
<xsl:for-each-group select="Account" group-by="@Category">
<xsl:sort select="@Category"/>
<Category name="{@Category}">
<xsl:for-each-group select="current-group()" group-by="@Region">
<xsl:sort select="@Region"/>
<Region name="{@Region}">
<xsl:for-each select="current-group()">
<xsl:sort select="@Name"/>
<Account>
<xsl:copy-of select="@Name,@Value,@Type,@Rating"/>
</Account>
</xsl:for-each>
</Region>
</xsl:for-each-group>
</Category>
</xsl:for-each-group>
</Accounts>
</xsl:template>
</xsl:stylesheet>
T:\ftemp>rem Done!