Xslt преобразование / как группировать сущности - PullRequest
1 голос
/ 06 июня 2011

Я хочу использовать XSLT для изменения:

<Office Code="1" OtherAttribute="5">
   <Customer CustomerId="0010" CodeModifier="A"/>
   <Customer CustomerId="0011" CodeModifier="B"/>
   <Customer CustomerId="0012" CodeModifier="B"/>
</Office>
<Office Code="2" OtherAttribute="6">
   <Customer CustomerId="2010" CodeModifier="A"/>
   <Customer CustomerId="0011" CodeModifier="C"/>
</Office>

в:

<Office Code="1A" OtherAttribute="5">
   <Customer CustomerId="0010"/>
</Office>
<Office Code="1B" OtherAttribute="5">
   <Customer CustomerId="0011"/>
   <Customer CustomerId="0012"/>
</Office>
<Office Code="2A" OtherAttribute="6">
   <Customer CustomerId="2010"/>
</Office>
<Office Code="2C" OtherAttribute="6">
   <Customer CustomerId="0011"/>
</Office>

Мои цели:

  • группирует каждый объект Customer с одним и тем же CodeModifier в объекты Office. Если есть несколько CodeModifier, я добавлю сущность Office. Атрибут Code в Office будет изменен (объединение CodeModifier клиента в Office)
  • (факультативный, но, я думаю, тривиальный) подавить атрибут CodeModifier и оставить все остальные атрибуты

Кто-нибудь знает, как это сделать?

Ответы [ 3 ]

2 голосов
/ 06 июня 2011

Эта таблица стилей, примененная к вашему входному примеру:

<!-- 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>
1 голос
/ 06 июня 2011

Полное и краткое решение XSLT 2.0 :

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/*/*">
  <xsl:for-each-group select="Customer" group-by="@CodeModifier">
   <Office>
     <xsl:copy-of select="../@*"/>
     <xsl:attribute name="Code" select=
         "concat(../@Code, current-grouping-key())"/>
     <xsl:copy-of select="current-group()"/>
   </Office>
  </xsl:for-each-group>
 </xsl:template>
</xsl:stylesheet>

применительно к следующему документу XML (на основе предоставленного фрагмента XML и простой его обертки)в один верхний элемент, чтобы сделать этот документ XML правильно сформированным):

<company>
    <Office Code="1" OtherAttribute="5">
        <Customer CustomerId="0010" CodeModifier="A"/>
        <Customer CustomerId="0011" CodeModifier="B"/>
        <Customer CustomerId="0012" CodeModifier="B"/>
    </Office>
    <Office Code="2" OtherAttribute="6">
        <Customer CustomerId="2010" CodeModifier="A"/>
        <Customer CustomerId="0011" CodeModifier="C"/>
    </Office>
</company>

дает требуемый, правильный результат :

<Office Code="1A" OtherAttribute="5">
   <Customer CustomerId="0010" CodeModifier="A"/>
</Office>
<Office Code="1B" OtherAttribute="5">
   <Customer CustomerId="0011" CodeModifier="B"/>
   <Customer CustomerId="0012" CodeModifier="B"/>
</Office>
    <Office Code="2A" OtherAttribute="6">
   <Customer CustomerId="2010" CodeModifier="A"/>
</Office>
<Office Code="2C" OtherAttribute="6">
   <Customer CustomerId="0011" CodeModifier="C"/>
</Office>
1 голос
/ 06 июня 2011

Вот еще один подход, использующий только соответствующие шаблоны.


Протестировано как XSLT 1.0 при MSXSL 4.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
    <xsl:output method="xml" indent="yes"/>

    <xsl:key name="kCustomerGroup" match="Customer" 
        use="concat(../@Code, @CodeModifier)" 
        />

    <xsl:template match="Office">
        <xsl:apply-templates select="Customer[generate-id() 
            =
            generate-id(key('kCustomerGroup', 
            concat(../@Code, @CodeModifier))[1])]"
            />
    </xsl:template>

    <xsl:template match="Customer">
        <Office 
            Code="{concat(../@Code,@CodeModifier)}" 
            OtherAttribute="{../@OtherAttribute}">

            <xsl:apply-templates select="key('kCustomerGroup', 
                concat(../@Code,@CodeModifier))" mode="copy"/>

        </Office>
    </xsl:template>

    <xsl:template match="Customer" mode="copy">
        <xsl:copy>
            <xsl:copy-of select="@CustomerId"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

Выходыдо:

<?xml version="1.0" encoding="UTF-8"?>
<Office Code="1A" OtherAttribute="5">
   <Customer CustomerId="0010"/>
</Office>
<Office Code="1B" OtherAttribute="5">
   <Customer CustomerId="0011"/>
   <Customer CustomerId="0012"/>
</Office>
<Office Code="2A" OtherAttribute="6">
   <Customer CustomerId="2010"/>
</Office>
<Office Code="2C" OtherAttribute="6">
   <Customer CustomerId="0011"/>
</Office>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...