2012-05-29 2 views
1

Я использую XSLT для преобразования XML, полученного из сети, и конвертировать то же самое «на лету» в целевой файл xml, обозначенный как вывод. Я все еще не могу сделать это даже после многого, может ли кто-нибудь помочь мне с этим преобразованием.Слияние и перестановка xml с использованием xslt

Источник XML

<allocelement> 
    <hd1>12</hd1> 
    <hd2>14</hd2> 
    <hd3>87</hd3> 
     <alc>1</alc> 
    <amount>4587</amount> 
    <code>1111</code> 
</allocelement> 
<alloclement> 
     <hd1>12</hd1> 
    <hd2>14</hd2> 
    <hd3>87</hd3> 
    <alc>2</alc> 
    <amount>80000</amount> 
    <code>1111</code> 
</alloclement> 
<alloclement> 
    <hd1>875</hd1> 
    <hd2>455</hd2> 
    <hd3>455</hd3> 
    <alc>2</alc> 
    <amount>80000</amount> 
    <code>1112</code> 
</alloclement> 

Выход Желаемая

<allocelement> 
    <Codeheader> 
    <code>1111</code> 
     <hd1>12</hd1> 
     <hd2>14</hd2> 
     <hd3>87</hd3> 
       <alc>1</alc> 
        <amount>4587</amount> 
       <alc>2</alc> 
        <amount>80000</amount> 
    </codeHeader> 
     <CodeHeader> 
    <code>1112</code> 
     <hd1>875</hd1> 
     <hd2>455</hd2> 
     <hd3>455</hd3> 
      <alc>2</alc> 
       <amount>80000</amount> 
    </CodeHeader> 
</allocelement> 

Группировка на основе Кодекса, [HD1, HD2, HD3] таким образом, что различные элементы, внутри которой имеют одинаковый код и [HD1, HD2, HD3] будут объединены и только показать поля, которые отличаются друг от друга. и. Также я использую xslt 1.0.

+0

Пожалуйста, после того, что вы делали до сих пор в XSLT. – toniedzwiedz

ответ

0

значительно короче и проще XSLT 1.0 Раствор:

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

<xsl:key name="kCodeByVal" match="code" use="."/> 

<xsl:template match="/*"> 
    <allocelement> 
    <xsl:apply-templates/> 
    </allocelement> 
</xsl:template> 

<xsl:template match="allocelement"/> 

<xsl:template match= 
    "allocelement 
    [generate-id(code) = generate-id(key('kCodeByVal', code)[1])]"> 
    <code><xsl:value-of select="code"/> 
    <xsl:copy-of select= 
    "node()[not(self::code)] 
    | key('kCodeByVal', code)/../*[self::alc or self::amount]"/> 
    </code> 
</xsl:template> 
</xsl:stylesheet> 

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

<t> 
    <allocelement> 
     <hd1>12</hd1> 
     <hd2>14</hd2> 
     <hd3>87</hd3> 
     <alc>1</alc> 
     <amount>4587</amount> 
     <code>1111</code> 
    </allocelement> 
    <allocelement> 
     <hd1>12</hd1> 
     <hd2>14</hd2> 
     <hd3>87</hd3> 
     <alc>2</alc> 
     <amount>80000</amount> 
     <code>1111</code> 
    </allocelement> 
    <allocelement> 
     <hd1>875</hd1> 
     <hd2>455</hd2> 
     <hd3>455</hd3> 
     <alc>2</alc> 
     <amount>80000</amount> 
     <code>1112</code> 
    </allocelement> 
</t> 

полезный, правильный результат получается:

<allocelement> 
    <code>1111<hd1>12</hd1> 
     <hd2>14</hd2> 
     <hd3>87</hd3> 
     <alc>1</alc> 
     <amount>4587</amount> 
     <alc>2</alc> 
     <amount>80000</amount> 
    </code> 
    <code>1112<hd1>875</hd1> 
     <hd2>455</hd2> 
     <hd3>455</hd3> 
     <alc>2</alc> 
     <amount>80000</amount> 
    </code> 
</allocelement> 

Пояснение: Правильное использование Muenchian grouping method.


II. 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:strip-space elements="*"/> 

    <xsl:template match="/*"> 
    <allocelement> 
    <xsl:for-each-group select="*" group-by="code"> 
    <code><xsl:value-of select="code"/> 
     <xsl:sequence select= 
     "node()[not(self::code)] 
     | current-group()/*[self::alc or self::amount]"/> 
    </code> 
    </xsl:for-each-group> 
    </allocelement> 
</xsl:template> 
</xsl:stylesheet> 

UPDATE: ОП изменил требования к outpu.

Вот соответствующий модифицированный раствор XSLT 1,0:

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

    <xsl:key name="kCodeByVal" match="code" use="."/> 

    <xsl:template match="/*"> 
     <allocelement> 
     <xsl:apply-templates/> 
     </allocelement> 
    </xsl:template> 

    <xsl:template match="allocelement"/> 

    <xsl:template match= 
     "allocelement 
     [generate-id(code) = generate-id(key('kCodeByVal', code)[1])]"> 
     <codeHeader> 
     <code><xsl:value-of select="code"/></code> 
     <xsl:copy-of select= 
     "node()[not(self::code)] 
     | key('kCodeByVal', code)/../*[self::alc or self::amount]"/> 
     </codeHeader> 
    </xsl:template> 
</xsl:stylesheet> 
+0

он короче и потому, что вы «жестко закодировали» имена узлов, которые не дублируются. Кроме того, можете ли вы сказать мне, где мое решение не является синтаксически или семантически правильным? :) –

+0

@PavelVeller: ваше решение XSLT использует неопределенный ключ и вызывает ошибку компиляции. Нужно обеспечить полную трансформацию - не только это устраняет любую возможную путаницу, но также показывает истинный размер кода. –

+0

@PavelVeller: решение короче (как и должно быть), потому что я использую функцию 'current-group()', как и все. Кроме того, все элементы-братья и сестры узла в этом случае могут быть выбраны с гораздо более коротким выражением - просто '../*[not self :: itsName]' –

0

Я бы предположил, что ваш вход XML имеет узел root, который обертывает все это, чтобы создать хорошо сформированный документ. Я также предполагаю, что все allocelement написаны правильно (у вас есть некоторые слова как alloclement). Я также предполагаю, что вы хотите удалить дубликаты, идентифицированные именем тега и его текстовым значением.

С XSLT 1.0:

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

    <xsl:key name="code" match="allocelement/code" use="."/> 

    <xsl:key name="code-sibling" 
      match="allocelement/*[name() != 'code']" 
      use="concat(parent::*/code, '|', name(), '|', .)"/> 

    <xsl:template match="@* | node()" name="copy"> 
     <xsl:copy> 
      <xsl:apply-templates select="@* | node()"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="/"> 
     <xsl:apply-templates select="root/allocelement[1]"/> 
    </xsl:template> 

    <xsl:template match="allocelement"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*"/> 
      <xsl:apply-templates 
        select="(. | following-sibling::allocelement)/code 
           [generate-id() = generate-id(key('code', .)[1])]"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="code"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*"/> 
      <xsl:value-of select="."/> 

      <xsl:apply-templates 
        select="key('code', .)/(preceding-sibling::* | following-sibling::*) 
           [generate-id() = 
           generate-id(key('code-sibling', concat(parent::*/code, '|', name(), '|', .))[1])]"/> 
     </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 

при нанесении:

<root> 
    <allocelement> 
     <hd1>12</hd1> 
     <hd2>14</hd2> 
     <hd3>87</hd3> 
     <alc>1</alc> 
     <amount>4587</amount> 
     <code>1111</code> 
    </allocelement> 
    <allocelement> 
     <hd1>12</hd1> 
     <hd2>14</hd2> 
     <hd3>87</hd3> 
     <alc>2</alc> 
     <amount>80000</amount> 
     <code>1111</code> 
    </allocelement> 
    <allocelement> 
     <hd1>875</hd1> 
     <hd2>455</hd2> 
     <hd3>455</hd3> 
     <alc>2</alc> 
     <amount>80000</amount> 
     <code>1112</code> 
    </allocelement> 
</root> 

производит:

<root> 
    <allocelement> 
     <code>1111<hd1>12</hd1> 
     <hd2>14</hd2> 
     <hd3>87</hd3> 
     <alc>1</alc> 
     <amount>4587</amount> 
     <alc>2</alc> 
     <amount>80000</amount> 
     </code> 
     <code>1112<hd1>875</hd1> 
     <hd2>455</hd2> 
     <hd3>455</hd3> 
     <alc>2</alc> 
     <amount>80000</amount> 
     </code> 
    </allocelement> 
</root> 

Вот как это работает. По умолчанию используется маршрут identity transform. Сначала мы прерываем allocelement и отправляем трансляцию по другому маршруту. Мы разрешаем ему копировать уникальный элемент code (уникальность, идентифицируемую текстовым значением элемента), с его значением, а затем применять шаблоны для всех предыдущих/следующих братьев и сестер всех узлов code, имеющих одинаковое значение. Теперь они будут дочерними узлами узла code. Затем мы вызываем преобразование идентичности для тех родственных братьев, которые являются единственными (относятся к code того же значения, имеют одно и то же имя и одно и то же текстовое значение).

1 примечание. MIXED content никогда не является хорошей идеей. Посмотрите, действительно ли вам нужно смешать текстовое значение и дочерние узлы под номером code.

С XSLT 2.0 вы могли бы остаться в стороне от generate-id() с:

<xsl:template match="allocelement"> 
    <xsl:copy> 
     <xsl:apply-templates select="@*"/> 
     <xsl:for-each-group select="(. | following-sibling::allocelement)/code" group-by="."> 
      <xsl:apply-templates select="."/> 
     </xsl:for-each-group> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="code"> 
    <xsl:copy> 
     <xsl:apply-templates select="@*"/> 
     <xsl:value-of select="."/> 

     <xsl:for-each-group select="key('code', .)/(preceding-sibling::* | following-sibling::*)" 
          group-by="concat(parent::*/code, '|', name(), '|', .)"> 
      <xsl:apply-templates select="."/> 
     </xsl:for-each-group> 
    </xsl:copy> 
</xsl:template> 
+0

Почему вы используете функцию 'key()' в решении XSLT 2.0 - это не определено? –

+0

@DimitreNovatchev, просто потому, что он был там в любом случае в версии 1.0. Я просто хотел проиллюстрировать 'xsl: for-each-group' –

+0

Pavel Veller: тогда вы можете прочитать о функции' current-group() '. Никто не использует 'key()' вместе с 'xsl: for-each-group', и одним из основных прецедентов для последнего было исключение необходимости использования ключей. –

Смежные вопросы