Xslt: сложное преобразование из Xml в Xml

  • Автор темы Kottt
  • Дата начала
K

Kottt

Гость
#1
Встала такая тестовая задачка.

1. Создать XSD схему с типами:

· Комплексный тип – Работник. С элементами: имя, пол, зарплата, название департамента;

· Комплексный тип – Отдел. Атрибуты: название отдела, средняя зарплата по отделу. Элемент: СписокРаботников;

· Комплексный тип – СписокИменРаботников. Элемент – имя работника.

2. Создать XML содержащий 5 работников. Причем 3 работника работают в одном и том же отделе, а остальные в других отделах.

3. Написать XSLT трансформацию, которая трансформирует XML файл со списком Работников в файл со списком Отделов.

Вот что я сделал:

****************************************

Файл firm.xml:

...

<!--shcode--><pre><code class='XML'><firm xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="firmScheme.xsd">

<employee>
<name>Sirik Sergei Alexandrovich</name>
<gender>male</gender>
<salary>600$</salary>
<depName>A02</depName>
</employee>
<employee>
<name>Ivanov Ivan Ivanovich</name>
<gender>male</gender>
<salary>500$</salary>
<depName>A02</depName>
</employee>
<employee>
<name>Petrov Ivan Denisovich</name>
<gender>male</gender>
<salary>620$</salary>
<depName>A02</depName>
</employee>
<employee>
<name>Kostukevich Sergi Alexandrovich</name>
<gender>male</gender>
<salary>450$</salary>
<depName>A07</depName>
</employee>
<employee>
<name>Lakizo Inna Anatolievna</name>
<gender>female</gender>
<salary>400$</salary>
<depName>OIC</depName>
</employee>
</firm>[/CODE]

*************************************************

Файл firmScheme.xsd:

...

<!--shcode--><pre><code class='XML'><xsd:element name="firm" type="firmType"/>

<xsd:element name="comment" type="xsd:string"/>

<xsd:complexType name="workerType">
<xsd:sequence>
<xsd:element name="name" type="xsd:string">
</xsd:element>
<xsd:element name="gender" type="xsd:string"/>
<xsd:element name="salary" type="xsd:string"/>
<xsd:element name="depName" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>

<xsd:complexType name="departmentType">
<xsd:sequence>
<xsd:element name="wokersList" type="xsd:string"/>
</xsd:sequence>
<xsd:attribute name="depName" type="xsd:string"/>
<xsd:attribute name="averageSalary" type="xsd:string"/>
</xsd:complexType>

<xsd:complexType name="nameListWorkersType">
<xsd:sequence>
<xsd:element name="workerName" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>

<xsd:complexType name="firmType">
<xsd:sequence>
<xsd:element name="employee" type="workerType" maxOccurs="unbounded" minOccurs="0"/>
<xsd:element name="dep" type="departmentType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="nameList" type="nameListWorkersType" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>

</xsd:schema>[/CODE]

********************************************************

Ну а с XSLT трансформацией с данными условиями - засада. Я, конечно, мало знаком с XSLT, но пару дней плотно посвятил изучению. И максимум получается трансформация, отображающая список отделов, где встречаются все отделы (даже если атрибут @depName у них одинаковый), ну и соответственно в элементе workersList присутствует только 1 работник.

Помогите, пожалуйста, решить задачку:)
 
K

Kottt

Гость
#2
Видимо задал сильно общий вопрос)))

ну, а если вот так:

Написал я вот такую трансформацию:

<!--shcode--><pre><code class='XML'><xsl:template match="firm">
<firm>
<xsl:apply-templates/>
</firm>
</xsl:template>
<xsl:template match="employee">
<dep>
<xsl:attribute name="depName">
<xsl:value-of select="depName"/>
</xsl:attribute>
<xsl:attribute name="averageSalary">
<xsl:value-of select="salary"/>
</xsl:attribute>
<xsl:apply-templates/>
</dep>
</xsl:template>
<xsl:template match="name">
<workersList>
<xsl:apply-templates/>
<xsl:apply-templates select="name"/>
</workersList>
</xsl:template>
<xsl:template match="gender"/>
<xsl:template match="depName"/>
<xsl:template match="salary"/>[/CODE]

И на выходе получается вот такой XML:

<!--shcode--><pre><code class='XML'><firm>
<dep depName="A02" averageSalary="600$">
<workersList>Sirik Sergei Alexandrovich</workersList>



</dep>
<dep depName="A02" averageSalary="500$">
<workersList>Ivanov Ivan Ivanovich</workersList>



</dep>
<dep depName="A02" averageSalary="620$">
<workersList>Petrov Ivan Denisovich</workersList>



</dep>
<dep depName="A07" averageSalary="450$">
<workersList>Kostukevich Sergi Alexandrovich</workersList>



</dep>
<dep depName="OIC" averageSalary="400$">
<workersList>Lakizo Inna Anatolievna</workersList>



</dep>
</firm>[/CODE]

И вот вопрос: Какое условие надо поставить чтобы выводило тег <dep/> с уникальным атрибутом @depName только 1 раз???

P.S.: ещё один маленький вопросик: Как избавиться от пустых строк в полученном XML???
 
K

Kottt

Гость
#3
Народ, ау. Тут че никто с XSLT толком не работал???

Помогите плз: Какое условие поставить вместо "?????" чтобы выводило только те теги <dep> у которых атрибут @depName уникален??? Текст преобразуемого XML и XSD см. выше.

<!--shcode--><pre><code class='XML'> <xsl:template match="employee">
<xsl:if test="?????">
<dep>
<xsl:attribute name="depName">
<xsl:value-of select="depName"/>
</xsl:attribute>
<xsl:attribute name="averageSalary">
<xsl:value-of select="salary"/>
</xsl:attribute>
<xsl:apply-templates/>
</dep>
</xsl:if>
</xsl:template>[/CODE]
 
K

Kottt

Гость
#5
Спасибо большое за ссылочки. Теперь получается вот так вот:

Трансформация:

<!--shcode--><pre><code class='XML'> <xsl:template match="/">
<firm>
<xsl:apply-templates/>
</firm>
</xsl:template>
<xsl:template match="firm">
<xsl:variable name="unique-list"
select="//depName[not(.=following::depName)]" />
<xsl:for-each select="$unique-list">
<dep>
<xsl:attribute name="depName">
<xsl:value-of select="."/>
</xsl:attribute>
<xsl:attribute name="averageSalary">
<xsl:value-of select="preceding-sibling::salary"/>
</xsl:attribute>
<workersList>
<xsl:apply-templates select="preceding-sibling::name" mode="workersList"/>
</workersList>
</dep>
</xsl:for-each>
</xsl:template>[/CODE]


На выходе:

<!--shcode--><pre><code class='XML'><firm>
<dep depName="A07" averageSalary="450$">
<workersList>Kostukevich Sergi Alexandrovich</workersList>
</dep>
<dep depName="OIC" averageSalary="400$">
<workersList>Lakizo Inna Anatolievna</workersList>
</dep>
<dep depName="A02" averageSalary="500$">
<workersList>Ivanov Ivan Ivanovich</workersList>
</dep>
</firm>[/CODE]

Добавлено: Теперь парюсь как сделать так чтобы в элементе <workersList/> были все имена работников, работающих в данном отделе и в аттрибуте @averageSalary была соответственно средняя зарплата по отделу. Пока безуспешно:) Буду очень благодарен за помощь.
 
K

Kottt

Гость
#6
Ну вот короче такая трансформация у меня получилась:

<!--shcode--><pre><code class='XML'><xsl:template match="/">
<firm>
<xsl:variable name="unique-list"
select="//depName[not(.=following::depName)]" />
<xsl:for-each select="$unique-list">
<dep>
<xsl:attribute name="depName">
<xsl:value-of select="."/>
</xsl:attribute>
<xsl:attribute name="averageSalary">
<xsl:choose>
<xsl:when test=".=//depName[.=following::depName]">
<xsl:apply-templates select="preceding-sibling::salary" mode="averageSalary"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="preceding-sibling::salary"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<workersList>
<xsl:value-of select="preceding-sibling::name"/>
<xsl:if test=".=//depName[.=following::depName]">
<xsl:apply-templates select="preceding-sibling::name" mode="workersList"/>
</xsl:if>
</workersList>
</dep>
</xsl:for-each>
</firm>
</xsl:template>

<xsl:template match="name" mode="workersList">
<xsl:variable name="notunique-list"
select="//depName[.=following::depName]" />
<xsl:for-each select="$notunique-list">
<xsl:text> </xsl:text>
<xsl:value-of select="preceding-sibling::name"/>
</xsl:for-each>
</xsl:template>

<xsl:template match="salary" mode="averageSalary">
<xsl:variable name="aSalary"
select="//depName[.=following::depName]/preceding-sibling::salary | //depName[.=preceding::depName]/preceding-sibling::salary" /> <xsl:value-of select="(sum($aSalary) div count($aSalary)) + ((sum($aSalary) mod count($aSalary))*1000 div count($aSalary)) div 1000"/>
</xsl:template>[/CODE]

И на выходе вот:

<!--shcode--><pre><code class='XML'><firm>
<dep depName="A07" averageSalary="450">
<workersList>Kostukevich Sergi Alexandrovich</workersList>
</dep>
<dep depName="OIC" averageSalary="400">
<workersList>Lakizo Inna Anatolievna</workersList>
</dep>
<dep depName="A02" averageSalary="556">
<workersList>Ivanov Ivan Ivanovich Sirik Sergei Alexandrovich Petrov Ivan Denisovich Fedorov Ivan Ivanovich</workersList>
</dep>
</firm>[/CODE]

Конечно кривовато сделано, но уж как получилось. Пожалуйста советуйте как улучшить. И есче вопросик: С расчетом средней зарплаты специально поубирал знаки обозначения валюты($) в исходном XML. Пробовал со знаками там используя substring-before() - не получилось. Если посоветуете как - буду очень признателен:)