2016-01-20 2 views
0

я в настоящее время есть файл XML, который выглядит следующим образом:Преобразование XML в группе полей в столбцы с помощью XSLT

<Plan> 
    <People> 
    <Person> 
     <name>Fred Bloggs</name> 
     <position>CEO</position> 
     <responsibility>Everything</responsibility> 
    </Person> 
    <Person> 
     <name>Joe Bloggs</name> 
     <position>Cleaner</position> 
     <responsibility>Cleaning</responsibility> 
    </Person> 
    <Person> 
     <name>Wilma Bloggs</name> 
     <position>CTO</position> 
     <responsibility>Tech stuff</responsibility> 
    </Person> 
    <Person> 
     <name>Betty Bloggs</name> 
     <position>MD</position> 
     <responsibility>Management</responsibility> 
    </Person> 
    </People> 
</Plan> 

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

Я хочу преобразовать и форматировать это, используя XSLT и XSL-FO, для создания PDF-файла, который имеет четыре столбца, причем каждая строка содержит имя поля, а затем значения поля для трех объектов Person. Например, так как есть четыре человека, перечисленные выше, я хочу, чтобы таблица выглядит следующим образом:

Name   Fred Bloggs Joe Bloggs Wilma Bloggs 
Position  CEO   Cleaner  CTO 
Responsibility Everything Cleaning Tech stuff 
Name   Betty Bloggs 
Position  MD 
Responsibility Management 

Эффективна, в I группы XML файла поле на человеке (так что каждый человек имеет три поля), в то время как в final output Я хочу группировать людей по полям (поэтому каждая строка имеет одно и то же поле для трех разных людей).

Если я пишу код XSL-FO вручную, я могу добиться этого, производя разметку вдоль этих линий для каждой строки:

<fo:table-row> 
    <fo:table-cell> 
    <fo:block>Name</fo:block> 
    </fo:table-cell> 
    <fo:table-cell> 
    <fo:block>Fred Bloggs</fo:block> 
    </fo:table-cell> 
    <fo:table-cell> 
    <fo:block>Joe Bloggs</fo:block> 
    </fo:table-cell> 
    <fo:table-cell> 
    <fo:block>Wilma Bloggs</fo:block> 
    </fo:table-cell> 
</fo:table-row> 

Это дает мне результат, я хочу, так что я знаю, что XSL-FO для части PDF работает нормально, но мне нужно автоматизировать процесс. Могу ли я достичь этого напрямую через XSLT?

Другой вариант я могу думать о том, чтобы произвести отдельный файл XML, который явно соответствует структуре макета, а затем преобразовать, что, например:

<PersonTable> 
    <PersonRow> 
    <name1>Fred Bloggs</name1> 
    <name2>Joe Bloggs</name2> 
    <name3>Wilma Bloggs</name3> 
    </PersonRow> 
</PersonTable> 

Конечное, наименее предпочтительным вариантом, будет производить полные данные XSL-FO автоматически, полностью перешагивая шаг трансформации (т.е. я эффективно перехожу от XSL-FO в PDF). Я не хочу этого делать, поскольку в конечном итоге я хотел бы иметь возможность отправлять различные файлы шаблонов XSL без изменения программного обеспечения, но я могу вернуться к этому варианту, если предыдущие не будут доступны.

+2

"Могу ли я достигнуть этого непосредственно через XSLT *?" Конечно, вы можете. Где именно вы застряли? –

+0

Я зациклился на том, как получить из записей X Person, каждый с Y-полями, в таблицу с 4 столбцами и Y строк для каждых трех записей Person. Если я хотел преобразовать записи Person в одну строку на человека и по одному столбцу на поле, я мог бы (и сделал) сделать это легко, но это не масштабируется до десятков полей, поскольку столбцы становятся слишком маленькими. – pwaring

+0

Можете ли вы изменить свой вопрос и добавить точный ожидаемый результат вашего примера ** в качестве кода **? –

ответ

1

Чтобы упростить этот вопрос (для меня), следующая таблица стилей создает простой HTML-таблицы в требуемом формате:

XSLT 1,0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> 
<xsl:strip-space elements="*"/> 

<xsl:param name="cols" select="3" /> 

<xsl:template match="People"> 
    <table border="1"> 
     <xsl:apply-templates select="Person[position() mod $cols = 1]"/> 
    </table> 
</xsl:template> 

<xsl:template match="Person"> 
    <xsl:variable name="group" select=". | following-sibling::Person[position() &lt; $cols]" /> 
    <xsl:for-each select="*"> 
     <xsl:variable name="i" select="position()" /> 
     <tr> 
      <td> 
       <xsl:value-of select="name()"/> 
      </td> 
      <xsl:for-each select="$group"> 
       <td> 
        <xsl:value-of select="*[$i]"/> 
       </td> 
      </xsl:for-each> 
     </tr> 
    </xsl:for-each> 
</xsl:template> 

</xsl:stylesheet> 

Применительно к вашему примеру, результат:

<?xml version="1.0" encoding="UTF-8"?> 
<table border="1"> 
    <tr> 
     <td>name</td> 
     <td>Fred Bloggs</td> 
     <td>Joe Bloggs</td> 
     <td>Wilma Bloggs</td> 
    </tr> 
    <tr> 
     <td>position</td> 
     <td>CEO</td> 
     <td>Cleaner</td> 
     <td>CTO</td> 
    </tr> 
    <tr> 
     <td>responsibility</td> 
     <td>Everything</td> 
     <td>Cleaning</td> 
     <td>Tech stuff</td> 
    </tr> 
    <tr> 
     <td>name</td> 
     <td>Betty Bloggs</td> 
    </tr> 
    <tr> 
     <td>position</td> 
     <td>MD</td> 
    </tr> 
    <tr> 
     <td>responsibility</td> 
     <td>Management</td> 
    </tr> 
</table> 

Вынесено как:

enter image description here

+0

Спасибо, я не совсем понимаю, как это работает, но этого достаточно, чтобы заставить меня двигаться (мне, вероятно, придется явно выводить каждую строку, поскольку порядок может измениться). – pwaring

1

Адаптирование мое решение от two adjacent tables in body-region each with two columns(xsl-fo) и до сих пор с помощью XSLT 1.0:

<xsl:param name="cols" select="3" /> 

    <xsl:template match="People"> 
    <fo:table> 
     <fo:table-body> 
     <xsl:call-template name="rows" /> 
     </fo:table-body> 
    </fo:table> 
    </xsl:template> 

    <xsl:template name="rows"> 
    <xsl:param name="persons" select="*" /> 

    <xsl:call-template name="row-group"> 
     <xsl:with-param name="persons" 
         select="$persons[position() &lt;= $cols]" /> 
    </xsl:call-template> 
    <xsl:if test="count($persons) > $cols"> 
     <xsl:call-template name="rows"> 
     <xsl:with-param name="persons" select="$persons[position() > $cols]" /> 
     </xsl:call-template> 
    </xsl:if> 
    </xsl:template> 

    <xsl:template name="row-group"> 
    <xsl:param name="persons" /> 

    <xsl:for-each select="$persons[1]/*"> 
     <xsl:variable name="position" select="position()" /> 
     <fo:table-row> 
     <fo:table-cell> 
      <fo:block><xsl:value-of select="local-name()" /></fo:block> 
     </fo:table-cell> 
     <xsl:for-each select="$persons"> 
      <fo:table-cell> 
      <fo:block><xsl:apply-templates select="./*[position()= $position]" /></fo:block> 
      </fo:table-cell> 
     </xsl:for-each> 
     </fo:table-row> 
    </xsl:for-each> 
    </xsl:template> 
+0

Спасибо, я могу использовать XSLT 1.0 или 2.0. Другой ответ был немного легче получить, и мне понравилась идея поместить количество столбцов в переменную, но я думаю, что это тоже сработало бы. – pwaring

+0

Я не знаю, о чем думал вчера. Я отредактировал XSLT, чтобы избавиться от жестко закодированных чисел. –

Смежные вопросы