XML в CSV с несколькими узлами XSLT

#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, не могли бы вы, пожалуйста, показать пример? Я проверяю, как использовать функцию, но у меня небольшая проблема с реализацией…