2017-01-10 3 views
0

У меня есть XML со следующей структурой:Как реструктурировать XML на основе двух условий?

<building> 
    <employee> 
     <zone>1</zone> 
     <system>A</system> 
     <name>Jhon</name> 
    </employee> 
    <employee> 
     <zone>1</zone> 
     <system>A</system> 
     <name>Paul</name> 
    </employee> 
    <employee> 
     <zone>1</zone> 
     <system>B</system> 
     <name>Matt</name> 
    </employee> 
    <employee> 
     <zone>2</zone> 
     <system>A</system> 
     <name>Bob</name> 
    </employee> 
    <employee> 
     <zone>2</zone> 
     <system>A</system> 
     <name>Peter</name> 
    </employee> 
</building> 

Сотрудники, отсортированных по зоне и системы по возрастанию.

Как я могу изменить структуру XML в следующем формате ?:

<building> 
    <block> 
     <employee> 
      <zone>1</zone> 
      <system>A</system> 
      <name>Jhon</name> 
     </employee> 
     <employee> 
      <zone>1</zone> 
      <system>A</system> 
      <name>Paul</name> 
     </employee> 
    </block> 
    <block> 
     <employee> 
      <zone>1</zone> 
      <system>B</system> 
      <name>Matt</name> 
     </employee> 
    </block> 
    <block> 
     <employee> 
      <zone>2</zone> 
      <system>A</system> 
      <name>Bob</name> 
     </employee> 
     <employee> 
      <zone>2</zone> 
      <system>A</system> 
      <name>Peter</name> 
     </employee> 
    </block> 
</building> 

Группировка условия по зонам и системы (для тех сотрудников, которые разделяют ту же зону и ту же систему).

Я пробовал с XSLT и XQuery без каких-либо успехов.

+2

Ну, вы говорите о «условиях группировки», так как насчет того, чтобы попробовать пример из https://www.w3.org/TR/xslt-30/#grouping-examples и адаптировать это или пример из спецификации XQuery ? –

+1

Соответствующая ссылка из спецификации XQuery: https://www.w3.org/TR/xquery-30/#id-group-by. – joewiz

ответ

3

Если вам нужно сгруппировать «2 условия», вы можете комбинировать эти условия для создания составного ключа.

Рекомендуется использовать разделитель, чтобы получить точные ключи. Объединение может быть выполнено с помощью concat() (XPath 1.0 или 2.0) или string-join() (только XPath 2.0).

Вот несколько примеров. Вы заметите, что во всех 3 я в основном используют то же самое, чтобы создать группировку ключей: concat(zone,'|',system)

XSLT 1.0 (или 2.0) (Muenchian группирования)

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

    <xsl:key name="employeeByBlock" match="employee" use="concat(zone,'|',system)"/> 

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

    <xsl:template match="/*"> 
    <xsl:copy> 
     <xsl:for-each select="employee[count(.|key('employeeByBlock',concat(zone,'|',system))[1])=1]"> 
     <xsl:sort select="zone"/> 
     <xsl:sort select="system" data-type="text"/> 
     <block> 
      <xsl:apply-templates select="key('employeeByBlock',concat(zone,'|',system))"> 
      <xsl:sort select="name" data-type="text"/> 
      </xsl:apply-templates> 
     </block> 
     </xsl:for-each>  
    </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 

XSLT 2.0 (xsl:for-each-group)

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

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

    <xsl:template match="/*"> 
    <xsl:copy> 
     <xsl:for-each-group select="employee" group-by="concat(zone,'|',system)"> 
     <xsl:sort select="zone"/> 
     <xsl:sort select="system" data-type="text"/>   
     <block> 
      <xsl:apply-templates select="current-group()"> 
      <xsl:sort select="name" data-type="text"/> 
      </xsl:apply-templates> 
     </block> 
     </xsl:for-each-group> 
    </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 

XQuery 3.0 (group by)

xquery version "3.0"; 
<building>{ 
for $employee in /building/employee 
let $zone := $employee/zone 
let $system := $employee/system 
group by $zone, $system 
order by $zone, $system 
return 
    <block>{ 
    for $x in $employee 
    order by $x/name 
    return $x 
    }</block> 
}</building> 

Все три из этих примеров дают одинаковый выход *:

<building> 
    <block> 
     <employee> 
     <zone>1</zone> 
     <system>A</system> 
     <name>Jhon</name> 
     </employee> 
     <employee> 
     <zone>1</zone> 
     <system>A</system> 
     <name>Paul</name> 
     </employee> 
    </block> 
    <block> 
     <employee> 
     <zone>1</zone> 
     <system>B</system> 
     <name>Matt</name> 
     </employee> 
    </block> 
    <block> 
     <employee> 
     <zone>2</zone> 
     <system>A</system> 
     <name>Bob</name> 
     </employee> 
     <employee> 
     <zone>2</zone> 
     <system>A</system> 
     <name>Peter</name> 
     </employee> 
    </block> 
</building> 

* Испытано XSLT 1.0 с Saxon 6.5.5. Протестированные XSLT 2.0 и XQuery с Saxon-HE 9.5.

+2

Есть ли необходимость в XQuery 3.0 для использования конкатенации строк для создания ключа из двух элементов? https://www.w3.org/TR/xquery-30/#id-group-by предлагает вам группировать по нескольким ключевым значениям непосредственно, например. 'group by $ zone: = $ employee/zone, $ system: = $ employee/system'. –

+0

@MartinHonnen - Хорошая добыча. Я только что скопировал/вставил. Я уверен, что ты прав. Я буду проверять реальный быстро, а затем обновить свой ответ. Благодаря! –

+0

@ DanielHaley большое спасибо !, версия XSLT 1.0 отлично работает для меня: D большие объятия !!! – lozanotux

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