#xml #xslt #csv
#xml #xslt #csv
Вопрос:
Я пытаюсь преобразовать XML-файл в формат CSV с помощью XSLT. Мой XML-файл имеет несколько корней (учетных записей) с атрибутами, а также с дочерними элементами разных типов.
Вот мой XML:
<SiebelMessage>
<ListOfSwiOrganizationIO>
<Account>
<Email>jozk8o@sk.ibm.com</Email>
<Name>ESET</Name>
<Address>
<StreetAddress>Vodna 9</StreetAddress>
<City>Nitra</City>
</Address>
<Contact>
<LastName>Jozo</LastName>
<FirstName>Sekera</FirstName>
</Contact>
<AccountCategory>
<CategoryValue>90</CategoryValue>
</AccountCategory>
<AccountCategory>
<CategoryValue>91</CategoryValue>
</AccountCategory>
<IntegrityCode>
<IntegrityType>AllowSms</IntegrityType>
<ConsentCode>YES</ConsentCode>
</IntegrityCode>
<IntegrityCode>
<IntegrityType>AllowEmail</IntegrityType>
<ConsentCode>YES</ConsentCode>
</IntegrityCode>
</Account>
<Account>
<Email>ferk8o@sk.ibm.com</Email>
<Name>IBM</Name>
<Address>
<StreetAddress>Dlha 1</StreetAddress>
<City>Bratislava</City>
</Address>
<Contact>
<LastName>Fero</LastName>
<FirstName>Kopacka</FirstName>
</Contact>
<AccountCategory>
<CategoryValue>90</CategoryValue>
</AccountCategory>
<AccountCategory>
<CategoryValue>91</CategoryValue>
</AccountCategory>
<IntegrityCode>
<IntegrityType>AllowSms</IntegrityType>
<ConsentCode>YES</ConsentCode>
</IntegrityCode>
<IntegrityCode>
<IntegrityType>AllowEmail</IntegrityType>
<ConsentCode>YES</ConsentCode>
</IntegrityCode>
</Account>
</ListOfSwiOrganizationIO>
Я смог создать XSLT на основе некоторых статей здесь, но отображающий только корневые атрибуты без дочерних элементов.
Требуемый вывод:
Email;Name;StreetAddress;City;LastName;FirstName;CategoryValue;AllowSms;AllowEmail
jozk8o@sk.ibm.com;ESET;Vodna 9;Nitra;Jozo;Sekera;90,91;YES;NOTSET
ferk8o@sk.ibm.com;IBM;Dlha 1;Bratislava;Fero;Kopacka;90,91;YES;NO
Вот мой текущий XSLT:
<xsl:output method="text" encoding="utf-8"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="delimiter" select="';'"/>
<xsl:template match="/">
<!-- Output the CSV header -->
<xsl:text>Email;Name;StreetAddress;City;LastName;FirstName;CategoryValue;AllowSms;AllowEmailamp;#10;</xsl:text>
<!-- Output the values -->
<xsl:for-each select="SiebelMessage/ListOfSwiOrganizationIO/Account">
<!-- begin values -->
<xsl:value-of select="concat(Email, $delimiter, Name, $delimiter, ListOfAddress/Address/StreetAddress, $delimiter, ListOfAddress/Address/City, $delimiter, Contact/LastName, $delimiter, Contact/FirstName, $delimiter)"/>
<!-- Category Values -->
<xsl:for-each select="AccountCategory">
<xsl:value-of select="CategoryValue"/>
<xsl:if test="position()!=last()">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:value-of select="$delimiter"/>
<!-- Integrity Codes -->
<xsl:value-of select="concat(IntegrityCode[IntegrityType='AllowSms']/ConsentCode, $delimiter, IntegrityCode[IntegrityType='AllowEmail']/ConsentCode)"/>
<xsl:choose>
<xsl:when test="ConsentCode=YES">
(1)
</xsl:when>
</xsl:choose>
<!-- end values -->
<xsl:if test="position()!=last()">
<xsl:text>amp;#10;</xsl:text>
</xsl:if>
</xsl:for-each>
Не могли бы вы посоветовать, как реализовать также дочерние элементы?
Мой текущий вывод — это только:
EMAIL;NAME
jozk8o@sk.ibm.com;ESET;
ferk8o@sk.ibm.com;IBM;
Большое вам спасибо
Комментарии:
1. Является ли структура
<Account>
постоянной и известной заранее?2. да, это так. будут и другие подузлы (дети учетной записи) Я упомянул лишь некоторые из них, чтобы упростить задачу.
Ответ №1:
Если структура постоянна и известна заранее, я бы посоветовал вам создать свой вывод, явно присвоив входным элементам имена:
XSLT 1.0
(отредактирован в соответствии с обновленным вопросом)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:variable name="delimiter" select="';'"/>
<xsl:template match="/">
<!-- Output the CSV header -->
<xsl:text>Email;Name;StreetAddress;City;LastName;FirstName;CategoryValue;AllowSms;AllowEmailamp;#10;</xsl:text>
<!-- Output the values -->
<xsl:for-each select="SiebelMessage/ListOfSwiOrganizationIO/Account">
<!-- begin values -->
<xsl:value-of select="concat(Email, $delimiter, Name, $delimiter, Address/StreetAddress, $delimiter, Address/City, $delimiter, Contact/LastName, $delimiter, Contact/FirstName, $delimiter)"/>
<!-- Category Values -->
<xsl:for-each select="AccountCategory">
<xsl:value-of select="CategoryValue"/>
<xsl:if test="position()!=last()">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:value-of select="$delimiter"/>
<!-- Integrity Codes -->
<xsl:value-of select="concat(IntegrityCode[IntegrityType='AllowSms']/ConsentCode, $delimiter, IntegrityCode[IntegrityType='AllowEmail']/ConsentCode)"/>
<!-- end values -->
<xsl:if test="position()!=last()">
<xsl:text>amp;#10;</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Преимущество этого заключается как в скорости, так и в отсутствии ненужных «движущихся частей».
Комментарии:
1. Спасибо, это помогло. Но есть еще 2 вещи. Я обновил XML и потребовал вывода в формате CSV. 1) Может быть более одной «AccountCategory», которые необходимо вставить в один столбец (в Excel), разделенный запятой (например, 90,91) 2) Может быть более одного «IntegrityCode». Но в заголовке будут фиксированные столбцы с именами атрибута «IntegrityType»(AllowSms;AllowEmail). Пожалуйста, смотрите выходные данные CSV. Не могли бы вы, пожалуйста, посоветовать, как реализовать эти последние 2 вещи? Большое вам спасибо
2. @user3804719 Смотрите правку к моему сообщению. — Примечание: Я не вижу значения «NOTSET» в ваших входных данных.
3. Уважаю Майкла, ты великолепен! Я просто забываю обновлять значения в XML. но может быть 3 значения: YES, NO, NOTSET. также можно было бы заменить в выводе 1,0″»? Я имею в виду заменить YES-> 1, NO-> 0, NOTSET-> «» (пустая строка).
4. @user3804719 Да ладно, это тривиально: просто используй
<xsl:choose>
.5. хм, я действительно новичок в XSL, не могли бы вы, пожалуйста, показать пример? Я проверяю, как использовать функцию, но у меня небольшая проблема с реализацией…