Эта таблица стилей, примененная к вашему входному примеру:
<!-- a key to group Customers by their office code + modifier -->
<xsl:key name="kCustomerGroup" match="Customer"
use="concat(../@Code, @CodeModifier)"
/>
<!-- identity template: copies everything that is not handled otherwise -->
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*" />
</xsl:copy>
</xsl:template>
<!-- apply templates directly to customers. modify as necessary -->
<xsl:template match="/">
<xsl:apply-templates select="//Customer" mode="CustomerGroup" />
</xsl:template>
<xsl:template match="Customer" mode="CustomerGroup">
<xsl:variable name="officeCode" select="concat(../@Code, @CodeModifier)" />
<!-- if this Customer is first of his respective group... -->
<xsl:if test="
generate-id()
=
generate-id(key('kCustomerGroup', $officeCode)[1])
">
<!-- use for-each to switch the context node to the parent -->
<xsl:for-each select="..">
<xsl:copy>
<xsl:apply-templates select="@*" />
<!-- overwrite the @Code attribute -->
<xsl:attribute name="Code">
<xsl:value-of select="$officeCode" />
</xsl:attribute>
<!-- now handle the Customer group members -->
<xsl:apply-templates select="key('kCustomerGroup', $officeCode)" />
</xsl:copy>
</xsl:for-each>
</xsl:if>
</xsl:template>
<!-- remove unwanted attribute with empty template -->
<xsl:template match="Customer/@CodeModifier" />
возвращает
<Office Code="1A" OtherAttribute="5">
<Customer CustomerId="0010"></Customer>
</Office>
<Office Code="1B" OtherAttribute="5">
<Customer CustomerId="0011"></Customer>
<Customer CustomerId="0012"></Customer>
</Office>
<Office Code="2A" OtherAttribute="6">
<Customer CustomerId="2010"></Customer>
</Office>
<Office Code="2C" OtherAttribute="6">
<Customer CustomerId="0011"></Customer>
</Office>
Примечание
- Использование шаблонных режимов.Я сделал шаблон специально для группировки
Customer
.Обычная обработка узлов происходит в шаблоне идентификатора. - Использование одноузлового цикла
xsl:for-each
для изменения узла контекста на xsl:copy
. - Использование пустого шаблона для удаленияузлы из вывода.
- То, что вы можете скопировать атрибуты с
xsl:copy
и still , перезаписать один из них позже. - То, что вывод находится в порядке документа.
Чтобы навязать определенный ордер, используйте что-то вроде этого:
<xsl:apply-templates select="//Customer" mode="CustomerGroup">
<xsl:sort select="../@Code" data-type="text" order="ascending" />
</xsl:apply-templates>
и
<xsl:apply-templates select="key('kCustomerGroup', $officeCode)">
<xsl:sort select="@CodeModifier" data-type="number" order="ascending" />
</xsl:apply-templates>