Преобразование XSLT1

Мне нужно будет преобразовать этот входной XML:

<Log1>
    <Connection User="Peter" Host="Computer01" Port="22"/>
    <Connection User="Peter" Host="Computer02" Port="22"/>
    <Connection User="Peter" Host="Computer02" Port="80"/>
    <Connection User="David" Host="Computer01" Port="8080"/>
    <Connection User="David" Host="Computer01" Port="8080"/>
    <Connection User="David" Host="Computer01" Port="8080"/>
    <Connection User="David" Host="Computer03" Port="22"/>
    <Connection User="David" Host="Computer04" Port="21"/>
</Log1>

В этот выходной XML:

<Log2>
    <Event Name="David" Target="Computer01|Computer03|Computer04"/>
    <Event Name="Peter" Target="Computer01|Computer02"/>            
</Log2>

До сих пор я использовал мюнхианскую группировку и получил что-то вроде:

<Log2>      
    <Event Name="David" Target="Computer01|Computer01|Computer01|Computer03|Computer04"/>   
    <Event Name="Peter" Target="Computer01|Computer02|computer02"/>
</Log2>

Проблема в том, что я не могу избавиться от повторяющихся машин в своем выводе.

Вот мой XSLT:

<xsl:key name="myKey" match="Connection" use="@User"/>
<xsl:variable name="separator" select="'|'"/>

<xsl:template match="Log1"> 
    <xsl:element name="Log2">       
        <xsl:for-each select="Connection[count(. | key('myKey', @User)[1]) = 1]">
            <xsl:sort select="@User" />
            <xsl:element name="Event">
                <xsl:attribute name="Name">
                    <xsl:value-of select="@User" />
                </xsl:attribute>
                <xsl:attribute name="Target">
                    <xsl:for-each select="key('myKey', @User)">
                        <xsl:sort select="@Host" />
                        <xsl:value-of select="@Host" />
                        <xsl:if test="position() != last()">
                            <xsl:value-of select="$separator" />            
                        </xsl:if>
                    </xsl:for-each>
                </xsl:attribute>
            </xsl:element>
        </xsl:for-each>         
    </xsl:element> 
</xsl:template>

Не могли бы вы, ребята, помочь мне с процессором XSLT 1.0?


person nereide    schedule 22.04.2015    source источник
comment
Покажите текущую таблицу стилей XSLT.   -  person Mathias Müller    schedule 22.04.2015
comment
Текущий XSLT добавлен, большое спасибо!   -  person nereide    schedule 22.04.2015


Ответы (1)


На самом деле вам нужно выполнить второй набор мюнхианской группировки здесь, потому что вам также нужно сгруппировать комбинацию «Пользователь» и «Хост», чтобы получить отдельный атрибут Host для каждого пользователя. Итак, вам нужен второй ключ, например:

<xsl:key name="myKey2" match="Connection" use="concat(@User, '|', @Host)"/>

(Обратите внимание, что канал | здесь может быть любым, если только он не встречается ни в User, ни в Host).

И затем вы используете в своем внутреннем xsl:for-each вот так:

<xsl:for-each select="key('myKey', @User)[count(. | key('myKey2', concat(@User, '|', @Host))[1]) = 1]">

Попробуйте этот XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:key name="myKey" match="Connection" use="@User"/>
<xsl:key name="myKey2" match="Connection" use="concat(@User, '|', @Host)"/>
<xsl:variable name="separator" select="'|'"/>

<xsl:template match="Log1"> 
    <xsl:element name="Log2">       
        <xsl:for-each select="Connection[count(. | key('myKey', @User)[1]) = 1]">
            <xsl:sort select="@User" />
            <xsl:element name="Event">
                <xsl:attribute name="Name">
                    <xsl:value-of select="@User" />
                </xsl:attribute>
                <xsl:attribute name="Target">
                    <xsl:for-each select="key('myKey', @User)[count(. | key('myKey2', concat(@User, '|', @Host))[1]) = 1]">
                        <xsl:sort select="@Host" />
                        <xsl:value-of select="@Host" />
                        <xsl:if test="position() != last()">
                            <xsl:value-of select="$separator" />            
                        </xsl:if>
                    </xsl:for-each>
                </xsl:attribute>
            </xsl:element>
        </xsl:for-each>         
    </xsl:element> 
</xsl:template>
</xsl:stylesheet>

В качестве примечания: нет реальной необходимости использовать xsl:element для создания элементов со статическими именами, просто запишите элемент напрямую. Например

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

<xsl:key name="myKey" match="Connection" use="@User"/>
<xsl:key name="myKey2" match="Connection" use="concat(@User, '|', @Host)"/>
<xsl:variable name="separator" select="'|'"/>

<xsl:template match="Log1"> 
    <Log2>       
        <xsl:for-each select="Connection[count(. | key('myKey', @User)[1]) = 1]">
            <xsl:sort select="@User" />
            <Event Name="{@User}">
                <xsl:attribute name="Target">
                    <xsl:for-each select="key('myKey', @User)[count(. | key('myKey2', concat(@User, '|', @Host))[1]) = 1]">
                        <xsl:sort select="@Host" />
                        <xsl:value-of select="@Host" />
                        <xsl:if test="position() != last()">
                            <xsl:value-of select="$separator" />            
                        </xsl:if>
                    </xsl:for-each>
                </xsl:attribute>
            </Event>
        </xsl:for-each>         
    </Log2> 
</xsl:template>
</xsl:stylesheet>

Также обратите внимание на использование шаблонов значений атрибутов при создании атрибута Name.

person Tim C    schedule 22.04.2015
comment
Привет, Тим, я только что протестировал твой XSLT, и он работает просто великолепно! Большое спасибо! - person nereide; 22.04.2015