XML в CSV для дочерних узлов

Приведенный ниже код XSLT позволяет конвертировать XML в CSV с заключенными в кавычки, но дочерний узел (Подробности) отображается в той же строке.

Обновление: Как уже упоминалось на форуме, я обновил вопрос с ожидаемым результатом.

XML-ввод

<?xml version = "1.0" encoding = "UTF-8"?>
 <Root>
  <Records>
    <TrRecords>
      <RecordID>0123</RecordID>
      <SequnceNumber>00001</SequnceNumber>
      <TNumber>00001</TNumber>
      <System>01</System>
      <Amount>4956.00</Amount>
     <Reference>T</Reference>
     <Account>89768</Account>
     <Name>bdi</Name>
    <Other2/>
    <Other3/>
    <Details>
   <RecordID>1234</RecordID>
  <SequnceNumber>00001</SequnceNumber>
   <TNumber>00001</TNumber>
  <Number>TI</Number>
<InvoiceDate>2024-05-05</InvoiceDate>
 <InvoiceDescription/>
 <InvoiceAmount>10.00</InvoiceAmount>
 </Details>
  <Details>
   <RecordID>1234</RecordID>
  <SequnceNumber>00002</SequnceNumber>
   <TNumber>00001</TNumber>
  <Number>TI3</Number>
<InvoiceDate>2024-05-05</InvoiceDate>
 <InvoiceDescription/>
 <InvoiceAmount>10.00</InvoiceAmount>
 </Details>
</TrRecords>
 <TrRecords>
 <RecordID>0123</RecordID>
 <SequnceNumber>00001</SequnceNumber>
 <TNumber>00002</TNumber>
 <System>01</System>
 <Amount>523.00</Amount>
 <Reference>TI</Reference>
 <Account>18907</Account>
<Name>elci</Name>
 <Other2/>
 <Other3/>
 <Details>
 <RecordID>1234</RecordID>
<SequnceNumber>00001</SequnceNumber>
 <TNumber>00001</TNumber>
  <Number>TIR</Number>
 <InvoiceDate>2024-08-17</InvoiceDate>
 <InvoiceDescription/>
 <InvoiceAmount>5245.00</InvoiceAmount>
   </Details>
  </TrRecords>
   <TrRecords>
    <RecordID>0123</RecordID>
   <Number>00001</Number>
   <TNumber>00003</TNumber>
    <System>01</System>
   <Amount>1180.00</Amount>
  <Reference>EMI</Reference>
<AccountNo>0936201002941</AccountNo>
   <Other2/>
    <Other3/>
 <Details>
  <RecordID>1234</RecordID>
  <SequnceNumber>00001</SequnceNumber>
   <TNumber>00001</TNumber>
   <Number>Tac</Number>
   <InvoiceDate>2024-08-17</InvoiceDate>
   <InvoiceDescription/>
    <InvoiceAmount>1180.00</InvoiceAmount>
     </Details>
  </TrRecords>
   </Records>
   </Root>

XSLT-код

<xsl:stylesheet version = "1.0" xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
  <xsl:output method = "text" encoding = "utf-8" />
  <xsl:param name = "delim" select = "','" />
  <xsl:param name = "quote" select = "'&quot;'" />
   <xsl:param name = "break" select = "'&#xA;'" />
  <xsl:template match = "/Root">
   <xsl:apply-templates select = "Records/TrRecords" />
   </xsl:template>
  <xsl:template match = "TrRecords">
   <xsl:apply-templates />
   <xsl:if test = "following-sibling::*">
    <xsl:value-of select = "$break" />
    </xsl:if>
  <xsl:for-each select = "Details">
  <xsl:if test = "following-sibling::*">
  <xsl:value-of select = "$break" />
   </xsl:if>
 </xsl:for-each>
 </xsl:template>
 <xsl:template match = "*">
<!-- remove normalize-space()  --> 
   <xsl:value-of select = "concat($quote, normalize-space(), $quote)" />
  <xsl:if test = "following-sibling::*">
  <xsl:value-of select = "$delim" />
   </xsl:if>
   </xsl:template>
  <xsl:template match = "text()" />
  </xsl:stylesheet>

Ожидаемый результат

"0123","00001","00001","01","4956.00","T","89768","bdi","",""
"1234","00001","00001","TI","2024-05-05","","10.00"
"1234","00002","00001","TI3","2024-05-05","","10.00"
"0123","00001","00002","01","523.00","TI","18907","elci","",""
"1234","00001","00001","TIR","2024-08-17","","5245.00"
"0123","00001","00003","01","1180.00","EMI","0936201002941","",""
"1234","00001","00001","Tac","2024-08-17","","1180.00"

В каждой строке, начиная с 1234, значения должны идти с новой строки и заключаться в кавычки. не могли бы вы помочь мне с этим?

С уважением, Джанардхан

Пожалуйста, отредактируйте свой вопрос и покажите точный результат, который вы хотите получить.

michael.hor257k 20.08.2024 20:38

@michael.hor257k Я обновил вопрос с ожидаемым результатом.

janardhan d 22.08.2024 22:06
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
65
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

(измененный)

Попробуйте что-то вроде:

<xsl:stylesheet version = "2.0" xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
<xsl:output method = "text" encoding = "utf-8" />
<xsl:strip-space elements = "*"/>
  
<xsl:param name = "delim" select = "','" />
<xsl:param name = "quote" select = "'&quot;'" />
<xsl:param name = "break" select = "'&#xA;'" />
   
<xsl:template match = "/Root">
    <xsl:apply-templates select = "Records/TrRecords" />
</xsl:template>
   
<xsl:template match = "TrRecords">
    <xsl:apply-templates select = "* except Details"/>
    <xsl:apply-templates select = "Details"/>
    <xsl:if test = "position()!=last()">
        <xsl:value-of select = "$break" />
    </xsl:if>
</xsl:template>
 
<xsl:template match = "Details">
    <xsl:value-of select = "$break" />
    <xsl:apply-templates/>
 </xsl:template>
 
<xsl:template match = "*">
    <xsl:value-of select = "concat($quote, normalize-space(), $quote)" />
    <xsl:if test = "position()!=last()">
        <xsl:value-of select = "$delim" />
    </xsl:if>
</xsl:template>
   
</xsl:stylesheet>

Демо здесь.

Обратите внимание, что это предполагает, что ни одно из значений, переданных на выход, не содержит кавычек.

Я пробовал использовать приведенный выше код, но в TrRecrods, если несколько сегментов сведений находятся в XML, то они отображаются в одной строке. Ниже приведен пример. "1234","00001","00001","TI","2024-05-05","","10.00","1234","‌​00003","00001","TI3"‌​ ,"2024-05-05","","10‌​.00"

janardhan d 22.08.2024 22:16

В исходном вопросе не было нескольких деталей. Я изменил свой ответ, но...

y.arazim 23.08.2024 02:59

Я попробовал обновленный код, и теперь записи сведений заполняются в одной строке TrRecords. Подробные записи должны быть в новой строке. "0123","00001","00001","01","4956.00","T","89768","bdi","","‌​","1234 00001 00001 TI 2024-05-05 10.00","1234 00002 00001 TI3 05.05.2024 10.00" "0123","00001","00002","01","523.00","TI","18907","elci","", ‌​"","1234 00001 00001 TIR 2024-08-17 5245.00" "0123","00001","00003","01","1180.00","EMI","0936201002941",‌​"", "","1234 00001 00001 Tac 17.08.2024 1180.00"

janardhan d 23.08.2024 03:29

Это не тот результат, который я получаю (см. ссылку в моем ответе).

y.arazim 23.08.2024 04:28

Проблема с моим приложением. Кажется, теперь он работает нормально. Большое спасибо за вашу помощь!

janardhan d 23.08.2024 15:54

Основная проблема заключается в том, что в шаблоне, который соответствует TrRecords, вы вызываете apply-templates для обработки всех дочерних элементов TrRecords, включая элементы Details, но затем позже вы снова явно перебираете эти Details элементы.

Вот таблица стилей с 4 шаблонами:

  • тот, который соответствует корню и применяет шаблоны к TrRecords,
  • тот, который соответствует элементам TrRecords и применяет шаблоны к их дочерним элементам
  • тот, который соответствует элементам Details и применяет шаблоны к своим дочерним элементам (обратите внимание: это гарантирует, что элемент Details, найденный внутри элемента TrRecords, обрабатывается как строка вывода, содержащая элементы, которые являются столбцами, а не обрабатывается как сам столбец)
  • тот, который соответствует любому другому элементу и выводит его как строку в кавычках

Обратите внимание: шаблон TrRecords добавляет разрыв строки после обработки дочерних элементов, а шаблон Details добавляет разрыв строки перед обработкой дочерних элементов.

Также обратите внимание, что строка $delim вставляется после рендеринга значения столбца, только если есть следующие одноуровневые элементы, кроме Details элементов.

<xsl:stylesheet version = "1.0" xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
   <xsl:output method = "text" encoding = "utf-8" />
   <xsl:param name = "delim" select = "','" />
   <xsl:param name = "quote" select = "'&quot;'" />
   <xsl:param name = "break" select = "'&#xA;'" />
   
   <xsl:template match = "/">
      <xsl:apply-templates select = "Root/Records/TrRecords" />
   </xsl:template>
   <xsl:template match = "TrRecords">
      <xsl:apply-templates select = "*"/>
      <xsl:value-of select = "$break" />
   </xsl:template>
   <xsl:template match = "Details">
      <xsl:value-of select = "$break" />
      <xsl:apply-templates select = "*"/>
   </xsl:template>
   <xsl:template match = "*">
      <xsl:value-of select = "concat($quote, ., $quote)" />
      <xsl:if test = "following-sibling::*[not(self::Details)]">
         <xsl:value-of select = "$delim" />
      </xsl:if>
   </xsl:template>
</xsl:stylesheet>

Однако лично я, если бы у меня был доступный интерпретатор XSLT 2, я был бы склонен написать это по-другому, используя только один шаблон и более сложное выражение XPath:

<xsl:stylesheet version = "2.0" xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
   <xsl:output method = "text" encoding = "utf-8" />
   <xsl:param name = "delim" select = "','" />
   <xsl:param name = "quote" select = "'&quot;'" />
   <xsl:param name = "break" select = "'&#xA;'" />
   
   <xsl:template match = "/">
      <xsl:value-of separator = "{$break}" select = "
        for $row in 
          (Root/Records/TrRecords | Root/Records/TrRecords/Details)
        return
          string-join(
            for $col in $row/*[not(self::Details)] return concat($quote, $col, $quote),
            $delim
          )
      "/>
   </xsl:template>
</xsl:stylesheet>

Другие вопросы по теме