Как просмотреть вложенную структуру XML с использованием шаблонов

#xml #xslt #nested

#xml #xslt #вложенный

Вопрос:

Я новичок в XSL и в процессе создания этого с нуля, чтобы решить проблему.

У меня есть исходный XML-файл, который содержит следующую структуру:-

 <root>
  <Header>

  </Header>

  <DetailRecord>
    <CustomerNumber>1</CustomerNumber>
    <DetailSubRecord>
      <Address>London</Address>
    </DetailSubRecord>
    <DetailSubRecord>
      <Address>Hull</Address>
    </DetailSubRecord>

  </DetailRecord>

  <DetailRecord>
    <CustomerNumber>2</CustomerNumber>
    <DetailSubRecord>
      <Address>Birmingham</Address>
    </DetailSubRecord>
    <DetailSubRecord>
      <Address>Manchester</Address>
    </DetailSubRecord>

  </DetailRecord>
  <Footer>

  </Footer>

</root>
  

где есть несколько <DetailRecord> файлов, каждый из которых содержит несколько <DetailSubRecord> файлов.

Мне удалось собрать XSL, который выводит единый вложенный набор из нескольких подробных записей в плоский файл, но я не смог разобраться, как ссылаться на 2-й вложенный уровень записей адресов в XSL…

Вот мой XSL на данный момент:

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:strip-space elements="*"/>
  <xsl:output method="text"/>
  <xsl:variable name="spaces" select="' '"/>
  <xsl:variable name="newline">
    <xsl:text>amp;#10;</xsl:text>
  </xsl:variable>
  <xsl:template match="/">
    <xsl:value-of select="root/Header/HeaderField"/>
    <xsl:copy-of select="$newline"/>
    <xsl:for-each select="root/DetailRecord">
      <xsl:value-of select="CustomerNumber"/>
      <xsl:copy-of select="$newline"/>
    </xsl:for-each>
    Trailer - recordCount - <xsl:value-of select="count(root/DetailRecord)"/>
  </xsl:template>
</xsl:stylesheet>
  

Комментарии:

1. Вот мой XSL на данный момент…

2. <xsl: элементы с пробелами =»*»/> <xsl: метод вывода =»текст»/> <xsl: имя переменной =»пробелы» select=»‘ ‘»/> <xsl: имя переменной = «перевод строки»> <xsl:text>amp;#10;</xsl:text> </xsl:переменная> <xsl: соответствие шаблону=»/»> <xsl: значение выбора=»root/Header/HeaderField»/> <xsl:копия выбора=»$newline»/ > <xsl:для каждого выбора =»root/DetailRecord»> <xsl: значение выбора =»CustomerNumber»/> <xsl: копия выбора =»$newline»/> </xsl: для каждого> Trailer — RecordCount — <xsl: значение выбора=»count(root/DetailRecord)»/> </xsl: шаблон> </xsl: таблица стилей>

3. Можете ли вы опубликовать пример вашего целевого вывода?

Ответ №1:

XSLT — это функциональный язык, а не процедурный; чего большинство новичков в XSLT не понимают, так это того, что процессор XSLT автоматически обрабатывает каждый узел в дереве в том порядке, в каком они отображаются в исходном коде. Однако без шаблона, определяющего, что делать с каждым узлом, ничего не выводится.

В большинстве случаев вам не нужно использовать <xsl:for-each> только для обработки дочерних элементов, это уже сделано за вас, вам просто нужно определить шаблон, который описывает, как вы хотите, чтобы каждый элемент выводился. Вот так:

 <xsl:template match="root">
  <xsl:apply-templates />
  <xsl:text>Trailer - recordCount - </xsl:text>
  <xsl:value-of select="count(DetailRecord)" />
</xsl:template>

<xsl:template match="HeaderField | CustomerNumber | Address">
  <xsl:value-of select="concat(.,$newline)" />
</xsl:template>

<xsl:template match="DetailSubRecord">
  <!-- do something with subrecord here -->
  <xsl:apply-templates />
</xsl:template>
  

<xsl:apply-templates /> В первом шаблоне просто сообщает процессору XSLT обработать дочерние элементы, после чего он добавляет количество записей.

Второй шаблон обрабатывает любой элемент с тремя именами в его match atrtibute, и в каждом случае выводит содержимое ( . ), объединенное с новой строкой.

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

Вы заметите, что это не дает никакой информации о том, как обращаться с DetailRecord элементом; поскольку все, что вы хотите сделать, это обработать его дочерние элементы, вам не нужно ничего указывать, поскольку это принимается как данность.

Ответ №2:

Здесь у вас есть простой пример того, как (буквально) применить шаблоны к вашей ситуации. Поскольку вы не очень четко описали требуемый вывод, я изобрел его.


XSLT 1.0 протестирован под Saxon 6.5.5

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:strip-space elements="*"/>
    <xsl:output method="text"/>

    <xsl:variable name="spaces" select="' '"/>
    <xsl:variable name="nl">
        <xsl:text>amp;#10;</xsl:text>
    </xsl:variable>

    <xsl:template match="/root">
        <xsl:apply-templates select="DetailRecord"/>
        <xsl:apply-templates select="Footer"/>
    </xsl:template>

    <xsl:template match="DetailRecord">
        <xsl:value-of select="concat(
            'Customer ',
            CustomerNumber, $nl)"
            />
        <xsl:apply-templates select="DetailSubRecord"/>
        <xsl:value-of select="concat(
            'Address Count:',
            count(DetailSubRecord),$nl,$nl
            )"
          />
    </xsl:template>

    <xsl:template match="DetailSubRecord">
        <xsl:value-of select="concat('-',Address,$nl)"/>
    </xsl:template>

    <xsl:template match="Footer">
        <xsl:value-of select="concat(
            'Customer Count:',
            count(preceding-sibling::DetailRecord),$nl
            )"
            />
    </xsl:template>

</xsl:stylesheet>
  

Примененный к вашему вводу, получает:

 Customer 1
-London
-Hull
Address Count:2

Customer 2
-Birmingham
-Manchester
Address Count:2

Customer Count:2