Muenchian? XSLT для денормализации / сводить / сглаживать XML-файл? - PullRequest
2 голосов
/ 21 апреля 2009

Имеется входной XML-файл со следующей структурой:

<root>
  <record row="1" col="1" val="1" />
  <record row="1" col="2" val="2" />
  <record row="1" col="3" val="3" />
  <record row="1" col="n" val="4" />
  <record row="2" col="1" val="5" />
  <record row="2" col="3" val="6" />
  <record row="2" col="n" val="7" />
  <record row="n" col="2" val="8" />
  <record row="n" col="3" val="9" />
  <record row="n" col="n" val="10" />
</root>

Как вывести следующую структуру, используя XSLT?

<root>
  <row id="1">
    <col id="1">1</col>
    <col id="2">2</col>
    <col id="3">3</col>
    <col id="n">4</col>
  </row>
  <row id="2">
    <col id="1">5</col>
    <col id="2"></col>
    <col id="3">6</col>
    <col id="n">7</col>
  </row>
  <row id="n">
    <col id="1"></col>
    <col id="2">8</col>
    <col id="3">9</col>
    <col id="n">10</col>
  </row>
</root>

[Обратите внимание, как выводятся все столбцы, даже если на входе нет связанных элементов]

РЕДАКТИРОВАТЬ: Возможно, я вызвал путаницу из-за использования цифр и букв в моем примере. Решение, которое я ищу, должно обрабатывать нечисловые атрибуты строк и столбцов.

Ответы [ 2 ]

3 голосов
/ 21 апреля 2009

Ответы на этот вопрос показывают возможные пути решения проблемы:

xslt: Как я могу использовать xslt для создания таблицы с несколькими столбцами и строками?


РЕДАКТИРОВАТЬ: Решение, которое включает в себя методы, представленные в связанном вопросе, следует.

Я предполагаю:

  • your @row и @col представляют собой инкрементные числа, которые определяют положение записи в таблице, и они не могут действительно содержать строку "n". Как таковые они не уникальны во всем документе, что делает их непригодными в качестве атрибутов HTML @id. Я заменил их @title атрибутами в моем выводе.
  • нет неявных пустых строк (пробелы в непрерывности @row не приведут к пустым строкам), только неявные пустые ячейки.
  • каждая комбинация @row и @col уникальна.

Это преобразование XSLT 1.0:

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

  <!-- prepare some keys for later use -->
  <xsl:key name="kRecordsByRow" match="record" use="@row" />
  <xsl:key name="kRecordsByPos" match="record" use="concat(@row, ',', @col)" />

  <!-- find out the highest @col number -->
  <xsl:variable name="vMaxCol">
    <xsl:for-each select="/root/record">
      <xsl:sort select="@col" data-type="number" order="descending" />
      <xsl:if test="position() = 1">
        <xsl:value-of select="@col" />
      </xsl:if>
    </xsl:for-each>
  </xsl:variable>

  <!-- select the <record>s that are the first in their rows -->
  <xsl:variable name="vRows" select="
    /root/record[
      generate-id()
      =
      generate-id(key('kRecordsByRow', @row)[1])
    ]
  " />  

  <!-- output basic table structure -->
  <xsl:template match="/root">
    <table>
      <xsl:for-each select="$vRows">
        <xsl:sort select="@row" data-type="number" />
        <tr title="{@row}">
          <xsl:call-template name="td" />
        </tr>
      </xsl:for-each>
    </table>
  </xsl:template>

  <!-- output the right number of <td>s in each row, empty or not -->
  <xsl:template name="td">
    <xsl:param name="col" select="1" />

    <td title="{$col}">
      <xsl:value-of select="key('kRecordsByPos', concat(@row, ',', $col))/@val" />
    </td>

    <xsl:if test="$col &lt; $vMaxCol">
      <xsl:call-template name="td">
        <xsl:with-param name="col" select="$col + 1" />
      </xsl:call-template>
    </xsl:if>
  </xsl:template>

</xsl:stylesheet>

… применительно к этому (слегка измененному) входу:

<root>
  <record row="1" col="1" val="1" />
  <record row="1" col="2" val="2" />
  <record row="1" col="3" val="3" />
  <record row="1" col="4" val="4" />
  <record row="2" col="1" val="5" />
  <record row="2" col="3" val="6" />
  <record row="2" col="4" val="7" />
  <record row="3" col="2" val="8" />
  <record row="3" col="3" val="9" />
  <record row="3" col="4" val="10" />
</root>

... производит:

<table>
  <tr title="1">
    <td title="1">1</td>
    <td title="2">2</td>
    <td title="3">3</td>
    <td title="4">4</td>
  </tr>
  <tr title="2">
    <td title="1">5</td>
    <td title="2"></td>
    <td title="3">6</td>
    <td title="4">7</td>
  </tr>
  <tr title="3">
    <td title="1"></td>
    <td title="2">8</td>
    <td title="3">9</td>
    <td title="4">10</td>
  </tr>
</table>
  • Muenchian группировка используется для выбора первых <record> с каждой @row группы
  • и <xsl:key> используется для точного определения записи по ее положению
  • рекурсия используется для создания согласованного набора <td> с, независимо от фактического существования <record> в названной позиции
2 голосов
/ 21 апреля 2009

Решение XSLT 2.0

Это преобразование :

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    >

    <xsl:output omit-xml-declaration="yes" indent="yes"/>

    <xsl:variable name="vDoc" as="document-node()"
     select="/"/>

    <xsl:key name="kColsByRow" match="@col" 
         use="../@row"/>

    <xsl:key name="kRecByRowCol" match="record" 
         use="concat(@row,'+',@col)"/>

    <xsl:template match="/*">
      <root>
        <xsl:for-each-group select="*/@row" 
             group-by=".">
          <xsl:sort select="current-grouping-key()"
               data-type="number"/>

           <xsl:variable name="vRow" 
                select="current-grouping-key()"/>  

           <row idd="{$vRow}">

              <xsl:for-each select=
               "1 to max(key('kColsByRow',$vRow)/xs:integer(.))">

                <col idd="{.}">
                  <xsl:value-of select=
                  "key('kRecByRowCol',
                        concat($vRow,'+',.),
                        $vDoc
                       )
                       /
                        @col
                "
                  />
                </col>
              </xsl:for-each>
           </row>    
        </xsl:for-each-group>
      </root>
    </xsl:template>
</xsl:stylesheet>

при применении к этому документу XML :

<root>
    <record row="1" col="1" val="1" />
    <record row="1" col="2" val="2" />
    <record row="1" col="3" val="3" />
    <record row="1" col="10" val="4" />
    <record row="2" col="1" val="5" />
    <record row="2" col="3" val="6" />
    <record row="2" col="10" val="7" />
    <record row="10" col="2" val="8" />
    <record row="10" col="3" val="9" />
    <record row="10" col="10" val="10" />
</root>

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

<root>
   <row idd="1">
      <col idd="1">1</col>
      <col idd="2">2</col>
      <col idd="3">3</col>
      <col idd="4"/>
      <col idd="5"/>
      <col idd="6"/>
      <col idd="7"/>
      <col idd="8"/>
      <col idd="9"/>
      <col idd="10">10</col>
   </row>
   <row idd="2">
      <col idd="1">1</col>
      <col idd="2"/>
      <col idd="3">3</col>
      <col idd="4"/>
      <col idd="5"/>
      <col idd="6"/>
      <col idd="7"/>
      <col idd="8"/>
      <col idd="9"/>
      <col idd="10">10</col>
   </row>
   <row idd="10">
      <col idd="1"/>
      <col idd="2">2</col>
      <col idd="3">3</col>
      <col idd="4"/>
      <col idd="5"/>
      <col idd="6"/>
      <col idd="7"/>
      <col idd="8"/>
      <col idd="9"/>
      <col idd="10">10</col>
   </row>
</root>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...