2013-03-16 6 views
4

Я нахожусь на своем пути с этим и задаюсь вопросом, возможно ли это с помощью XSL. Допустим, что у меня есть этот XML данные:Повторите первый узел из файла XML с помощью XSL

<?xml version="1.0" standalone="yes"?> 
<Data> 
    <Row> 
    <F1>Created By</F1>  
    <F2>City</F2> 
    </Row> 
    <Row> 
    <F1>John Doe</F1> 
    <F2>Los Angeles</F2> 
    </Row> 
    <Row> 
    <F1>Jane Doe</F1> 
    <F2>San Diego</F2> 
    </Row> 
</Data> 

Я хотел бы повторить первый элемент строки, а переборе остальных данных. Короче говоря, я хотел бы, чтобы этот результат был следующим:

[Created By] [City] 
----------- ------------ 
[John Doe] [Los Angeles] 

[Created By] [City] 
---------- ------------ 
[Jane Doe] [San Diego] 

Какой был бы наилучший подход? Я попытался установить первый элемент «Создано» как переменную, но я не выдаю его, когда пытаюсь его использовать. Я довольно новичок в XSL, и любая помощь будет оценена по достоинству.

Спасибо

+0

ли вы на самом деле хотите, открытый текст, как, что, как выход, или вы хотите, это как HTML? Если да, не могли бы вы показать нам, что вы хотите, чтобы HTML (сам код) выглядел? – JLRishe

+0

Мне нравится простой текст. Я просто обновил вывод из моего вопроса. – user2177441

+0

Хорошо, спасибо за разъяснение. Итак, вы хотите скобки вокруг имен столбцов или имена столбцов _and_ значения? – JLRishe

ответ

4

Вот решение, что, в отличие от в настоящее время общепринятый ответ, обрабатывает правильно данные с различными, заранее неизвестно, длина - см дополнительно обновление, которое обрабатывает неограниченное число столбцов, слишком:

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

<xsl:variable name="vMax1"> 
    <xsl:call-template name="maxLength"> 
    <xsl:with-param name="pNodes" select="/*/*/*[1]"/> 
    </xsl:call-template> 
</xsl:variable> 
<xsl:variable name="vMax2"> 
    <xsl:call-template name="maxLength"> 
    <xsl:with-param name="pNodes" select="/*/*/*[2]"/> 
    </xsl:call-template> 
</xsl:variable> 

<xsl:variable name="vLongest1" select= 
    "/*/*/*[1][string-length() = $vMax1][1]"/> 
<xsl:variable name="vLongest2" select= 
    "/*/*/*[2][string-length() = $vMax2][1]"/> 

<xsl:variable name="vUnderscores1" select= 
    "concat('__', 
      translate($vLongest1,translate($vLongest1, '_', ''), 
        '_________________________________________________________') 
     )"/> 
<xsl:variable name="vUnderscores2" select= 
    "concat('__', 
      translate($vLongest2,translate($vLongest2, '_', ''), 
        '_________________________________________________________') 
     )"/> 
<xsl:variable name="vBlanks1" 
    select="translate($vUnderscores1,'_', ' ')"/> 
<xsl:variable name="vBlanks2" 
    select="translate($vUnderscores2,'_', ' ')"/> 

<xsl:variable name="vTitle1" select= 
"concat('[',/*/*/*[1],']', 
     substring($vBlanks1,1, 
        string-length($vBlanks1)-string-length(/*/*/*[1])) 
     )"/> 
<xsl:variable name="vTitle2" select= 
"concat('[',/*/*/*[2],']', 
     substring($vBlanks2,1, 
        string-length($vBlanks2)-string-length(/*/*/*[2])) 
     )"/> 

<xsl:template match="Row"> 
    <xsl:value-of select= 
    "concat('&#xA;', $vTitle1, $vTitle2)"/> 
    <xsl:value-of select= 
    "concat('&#xA;',$vUnderscores1, ' ', $vUnderscores2, '&#xA;')"/> 
    <xsl:value-of select= 
    "concat(F1, 
      substring($vBlanks1,1, 
        string-length($vBlanks1)-string-length(F1)), 
      ' ', 
      F2, 
      substring($vBlanks1,1, 
        string-length($vBlanks1)-string-length(F2)), 
      '&#xA;' 
     )"/> 
</xsl:template> 

<xsl:template name="maxLength"> 
    <xsl:param name="pNodes" select="/.."/> 

    <xsl:for-each select="$pNodes"> 
    <xsl:sort select="string-length()" 
     data-type="number" order="descending"/> 
    <xsl:if test="position() = 1"> 
    <xsl:value-of select="string-length()"/> 
    </xsl:if> 
    </xsl:for-each> 
</xsl:template> 
<xsl:template match="Row[1]|text()"/> 
</xsl:stylesheet> 

Когда это преобразование применяются на предоставленном документе XML:

<Data> 
    <Row> 
    <F1>Created By</F1> 
    <F2>City</F2> 
    </Row> 
    <Row> 
    <F1>John Doe</F1> 
    <F2>Los Angeles</F2> 
    </Row> 
    <Row> 
    <F1>Jane Doe</F1> 
    <F2>San Diego</F2> 
    </Row> 
</Data> 

разыскиваемых, правильный результат получается:

[Created By] [City]   
____________ _____________ 
John Doe  Los Angeles 

[Created By] [City]   
____________ _____________ 
Jane Doe  San Diego 

Более интересно, при нанесении на этом XML-документа:

<Data> 
    <Row> 
     <F1>Created By</F1> 
     <F2>City</F2> 
    </Row> 
    <Row> 
     <F1>John Doe</F1> 
     <F2>La Villa Real de la Santa Fe de San Francisco de Asis</F2> 
    </Row> 
    <Row> 
     <F1>Josiah Willard Gibbs</F1> 
     <F2>San Diego</F2> 
    </Row> 
</Data> 

снова правильные результаты получают:

[Created By]   [City]             
______________________ _______________________________________________________ 
John Doe    La Villa Real de la Santa Fe de San Francisco de Asis 

[Created By]   [City]             
______________________ _______________________________________________________ 
Josiah Willard Gibbs San Diego    

Сравните этот корр. ЭСТ результат с тем, производимого в настоящее время общепринятый ответ:

Created By City   
----------- ----------- 
John Doe La Villa Real de la Santa Fe de San Francisco de Asis 

Created By City   
----------- ----------- 
Josiah Willard GibbsSan Diego 

II 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"> 
<xsl:output method="text"/> 

<xsl:variable name="vMax" as="xs:integer*" select= 
    "for $i in 1 to 2 
    return 
     max(/*/*/*[$i]/string-length(.))"/> 

<xsl:variable name="vUnderscores" select= 
    "for $i in 1 to 2 
    return 
      concat('__', 
        string-join((for $len in 1 to $vMax[$i] 
              return '_'), '') 
       )"/> 

<xsl:variable name="vBlanks" select= 
    "for $i in 1 to 2 
    return 
     translate($vUnderscores[$i],'_', ' ') 

    "/> 

<xsl:variable name="vTitle" select= 
    "for $i in 1 to 2 
    return 
     concat('[',(*/*/*)[$i],']', 
     substring($vBlanks[$i],1, 
        $vMax[$i]+2 -string-length((/*/*/*)[$i])) 
     ) 

    "/> 
<xsl:template match="Row"> 
    <xsl:value-of select= 
    "concat('&#xA;', $vTitle[1], $vTitle[2])"/> 
    <xsl:value-of select= 
    "concat('&#xA;',$vUnderscores[1], ' ', $vUnderscores[2], '&#xA;')"/> 
    <xsl:value-of select= 
    "concat(F1, 
      substring($vBlanks[1],1, 
        $vMax[1]+2 -string-length(F1)), 
      ' ', 
      F2, 
      substring($vBlanks[2],1, 
        $vMax[1]+2 -string-length(F2)), 
      '&#xA;' 
     )"/> 
</xsl:template> 
<xsl:template match="Row[1]|text()"/> 
</xsl:stylesheet> 

III. Обработка idefinite количество столбцов:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:xs="http://www.w3.org/2001/XMLSchema"> 
<xsl:output method="text"/> 

<xsl:variable name="vNumCols" select="max(/*/*/count(*))"/> 

<xsl:variable name="vMax" as="xs:integer*" select= 
    "for $i in 1 to $vNumCols 
    return 
     max(/*/*/*[$i]/string-length(.))"/> 

<xsl:variable name="vUnderscores" select= 
    "for $i in 1 to $vNumCols 
    return 
     concat('__', 
       string-join((for $len in 1 to $vMax[$i] 
          return '_'), '') 
      )"/> 

<xsl:variable name="vBlanks" select= 
    "for $i in 1 to $vNumCols 
    return 
     translate($vUnderscores[$i],'_', ' ') 

    "/> 

<xsl:variable name="vTitle" select= 
    "for $i in 1 to $vNumCols 
    return 
     concat('[',(*/*/*)[$i],']', 
     substring($vBlanks[$i],1, 
        $vMax[$i]+2 -string-length((/*/*/*)[$i])) 
     ) 

    "/> 
<xsl:template match="Row"> 
    <xsl:value-of separator="" select= 
    "'&#xA;', string-join($vTitle, '')"/> 
    <xsl:value-of separator="" select= 
    "'&#xA;', string-join($vUnderscores, ' '), '&#xA;'"/> 

    <xsl:value-of select= 
    "string-join((for $i in 1 to $vNumCols, 
       $vChild in *[$i] 
      return 
       ($vChild, 
       substring($vBlanks[$i],1, 
         $vMax[$i]+2 -string-length($vChild) 
         ), 
       ' ' 
        ), 
       '&#xA;' 
       ), 
       '' 
       )"/> 
</xsl:template> 
<xsl:template match="Row[1]|text()"/> 
</xsl:stylesheet> 

При применении этого XML-документа (3 колонки):

<Data> 
    <Row> 
    <F1>Created By</F1> 
    <F2>City</F2> 
    <F3>Region</F3> 
    </Row> 
    <Row> 
    <F1>Pablo Diego Ruiz y Picasso</F1> 
    <F2>Los Angeles</F2> 
    <F3>CA</F3> 
    </Row> 
    <Row> 
    <F1>Jane Doe</F1> 
    <F2>La Villa Real de la Santa Fe de San Francisco de Asis</F2> 
    <F3>NM</F3> 
    </Row> 
</Data> 

разыскиваемый, правильный результат получается:

[Created By]     [City]             [Region] 
____________________________ _______________________________________________________ ________ 
Pablo Diego Ruiz y Picasso Los Angeles            CA   

[Created By]     [City]             [Region] 
____________________________ _______________________________________________________ ________ 
Jane Doe      La Villa Real de la Santa Fe de San Francisco de Asis NM  

IV. Перевод в XSLT 1.0 Части III (выше):

Обратите внимание:

решение, которое JLRishe обеспеченное как исправление его первоначального ответа, будет выполнять своего рода 400 раз, если имеется 100 строк, каждая с 4 столбца.

Нет такой неэффективности в коде ниже:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:ext="http://exslt.org/common"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 

<xsl:variable name="vMaxCols"> 
    <xsl:call-template name="maxChildren"> 
    <xsl:with-param name="pNodes" select="/*/*"/> 
    </xsl:call-template> 
</xsl:variable> 

<xsl:variable name="vrtfMax"> 
    <xsl:for-each select="(/*/*/*)[not(position() > $vMaxCols)]"> 
    <xsl:variable name="vPos" select="position()"/> 
    <length> 
    <xsl:call-template name="maxLength"> 
     <xsl:with-param name="pNodes" select="/*/*/*[position()=$vPos]"/> 
    </xsl:call-template> 
    </length> 
    </xsl:for-each> 
</xsl:variable> 

<xsl:variable name="vMax" select="ext:node-set($vrtfMax)/length"/> 

<xsl:variable name="vrtfUnderscores"> 
    <xsl:for-each select="(/*/*/*)[not(position() > $vMaxCols)]"> 
    <xsl:variable name="vPos" select="position()"/> 
    <xsl:variable name="vLongestDataNode" select= 
    "/*/*/*[position()=$vPos 
      and string-length() = $vMax[position() = $vPos]][1]"/> 
    <t> 
     <xsl:value-of select= 
     "concat('__', 
        translate($vLongestDataNode,translate($vLongestDataNode, '_', ''), 
          '_________________________________________________________') 
       )"/> 
    </t> 
    </xsl:for-each> 
</xsl:variable> 

<xsl:variable name="vUnderscores" select="ext:node-set($vrtfUnderscores)/t"/> 

<xsl:variable name="vrtfBlanks"> 
    <xsl:for-each select="$vUnderscores"> 
    <xsl:variable name="vPos" select="position()"/> 
    <t><xsl:value-of select= 
      "translate($vUnderscores[position()=$vPos],'_', ' ')"/> 
    </t> 
    </xsl:for-each> 
</xsl:variable> 

<xsl:variable name="vBlanks" select="ext:node-set($vrtfBlanks)/t"/> 

<xsl:variable name="vrtfTitle"> 
    <xsl:for-each select="/*/*[1]/*"> 
    <xsl:variable name="vPos" select="position()"/> 
    <t> 
    <xsl:value-of select= 
    "concat('[',.,']', 
     substring($vBlanks[position()=$vPos],1, 
        2+$vMax[position()=$vPos]-string-length()) 
     ) 
    "/> 
    </t> 
    </xsl:for-each> 
</xsl:variable> 

<xsl:variable name="vTitle" select="ext:node-set($vrtfTitle)/t"/> 

<xsl:template match="Row"> 
    <xsl:text>&#xA;</xsl:text> 
    <xsl:for-each select="$vTitle"> 
     <xsl:value-of select="."/> 
    </xsl:for-each> 

    <xsl:text>&#xA;</xsl:text> 
    <xsl:for-each select="$vUnderscores"> 
     <xsl:value-of select="."/> 
     <xsl:text> </xsl:text> 
    </xsl:for-each> 
    <xsl:text>&#xA;</xsl:text> 

    <xsl:for-each select="*[not(position() > $vMaxCols)]"> 
    <xsl:variable name="vPos" select="position()"/> 

    <xsl:value-of select= 
    "concat(., 
      substring($vBlanks[position()=$vPos], 
        1, 
        string-length($vBlanks[position()=$vPos]) 
        -string-length()), 
      ' ' 
      )"/> 
    </xsl:for-each> 
    <xsl:text>&#xA;</xsl:text> 
</xsl:template> 

<xsl:template name="maxChildren"> 
    <xsl:param name="pNodes" select="/.."/> 

    <xsl:for-each select="$pNodes"> 
    <xsl:sort select="count(*)" 
     data-type="number" order="descending"/> 
    <xsl:if test="position() = 1"> 
    <xsl:value-of select="count(*)"/> 
    </xsl:if> 
    </xsl:for-each> 
</xsl:template> 

<xsl:template name="maxLength"> 
    <xsl:param name="pNodes" select="/.."/> 

    <xsl:for-each select="$pNodes"> 
    <xsl:sort select="string-length()" 
     data-type="number" order="descending"/> 
    <xsl:if test="position() = 1"> 
    <xsl:value-of select="string-length()"/> 
    </xsl:if> 
    </xsl:for-each> 
</xsl:template> 
<xsl:template match="Row[1]|text()"/> 
</xsl:stylesheet> 
+0

Спасибо Димитр! Очень приятно! ... Я просто попробовал и добавил больше столбцов. – user2177441

+0

@ user2177441, добро пожаловать. –

+0

Этот ответ не может обрабатывать более двух столбцов, а места элементов столбца жестко закодированы в некоторых местах, а не в других. Я бы подумал, что динамическая обработка столбцов будет более приоритетной, чем динамическая обработка их ширины. Мой измененный ответ делает оба. – JLRishe

2

Будет ли это хватай:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
    <xsl:output method="text"/> 
    <xsl:key name="kColumn" match="Row/*" use="count(preceding-sibling::*) + 1" /> 

    <xsl:param name="columnWidth" select="13" /> 

    <xsl:variable name="firstRow" select="/Data/Row[1]" /> 
    <xsl:variable name="spaces" 
       select="'                   '" /> 
    <xsl:variable name="separatorChars" 
       select="'---------------------------------------------------------------------------'" /> 

    <xsl:template match="/*"> 
    <xsl:apply-templates select="Row[position() > 1]" /> 
    </xsl:template> 

    <xsl:template match="Row"> 
    <xsl:apply-templates select="$firstRow/*" /> 
    <xsl:text>&#xA;</xsl:text> 
    <xsl:apply-templates select="$firstRow/*" mode="separator" /> 
    <xsl:text>&#xA;</xsl:text> 
    <xsl:apply-templates select="*"/> 
    <xsl:text>&#xA;&#xA;</xsl:text> 
    </xsl:template> 

    <xsl:template match="Row/*" name="Cell"> 
    <xsl:param name="value" select="concat('[', ., ']')" /> 
    <xsl:param name="width"> 
     <xsl:call-template name="FindWidth" /> 
    </xsl:param> 

    <xsl:variable name="numSpaces" 
        select="$width + 1 - string-length($value)" /> 
    <xsl:variable name="trailingSpace" select="substring($spaces, 1, $numSpaces)" /> 
    <xsl:value-of select="concat($value, $trailingSpace)"/> 
    </xsl:template> 

    <xsl:template match="Row/*" mode="separator"> 
    <xsl:variable name="width"> 
     <xsl:call-template name="FindWidth" /> 
    </xsl:variable> 
    <xsl:call-template name="Cell"> 
     <xsl:with-param name="value" select="substring($separatorChars, 1, $width)" /> 
     <xsl:with-param name="width" select="$width" /> 
    </xsl:call-template> 
    </xsl:template> 

    <xsl:template name="FindWidth"> 
    <xsl:apply-templates select="key('kColumn', position())" mode="findLength"> 
     <xsl:sort select="string-length()" data-type="number" order="descending" /> 
    </xsl:apply-templates> 
    </xsl:template> 

    <xsl:template match="Row/*" mode="findLength"> 
    <xsl:if test="position() = 1"> 
     <xsl:variable name="len" select="string-length() + 2" /> 
     <xsl:value-of select="$len * ($len > $columnWidth) + $columnWidth * ($columnWidth > $len)"/> 
    </xsl:if> 
    </xsl:template> 
</xsl:stylesheet> 

При запуске на этом входе:

<Data> 
    <Row> 
    <F1>Created By</F1> 
    <F2>City</F2> 
    <F3>Region</F3> 
    </Row> 
    <Row> 
    <F1>Pablo Diego Ruiz y Picasso</F1> 
    <F2>Los Angeles</F2> 
    <F3>CA</F3> 
    </Row> 
    <Row> 
    <F1>Jane Doe</F1> 
    <F2>La Villa Real de la Santa Fe de San Francisco de Asis</F2> 
    <F3>NM</F3> 
    </Row> 
</Data> 

Он производит:

[Created By]     [City]             [Region]  
---------------------------- ------------------------------------------------------- ------------- 
[Pablo Diego Ruiz y Picasso] [Los Angeles]           [CA]   

[Created By]     [City]             [Region]  
---------------------------- ------------------------------------------------------- ------------- 
[Jane Doe]     [La Villa Real de la Santa Fe de San Francisco de Asis] [NM]   
1

Попробуйте это:

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

    <!-- Output HTML - as an example --> 
    <xsl:output method="html" indent="yes"/> 

    <!-- Template that skips the first row, so it does not get processed --> 
    <xsl:template match="Row[position()=1]"> 
    </xsl:template> 

    <!-- Template that process all rows except the first one --> 
    <xsl:template match="Row[position()>1]"> 
    <!-- Copy the first row--> 
    <tr> 
     <xsl:for-each select="../Row[1]/*"> 
     <td> 
      <xsl:value-of select="."/> 
     </td> 
     </xsl:for-each> 
    </tr> 
    <!-- Process this row --> 
    <tr> 
     <xsl:for-each select="*"> 
     <td> 
      <xsl:value-of select="."/> 
     </td> 
     </xsl:for-each> 
    </tr> 
    </xsl:template> 

    <!-- Root template: output the HTML scaffolding and then processes the individual rows --> 
    <xsl:template match="/"> 
    <html> 
     <head></head> 
     <body> 
     <table> 
      <xsl:apply-templates/> 
     </table> 
     </body> 
    </html> 
    </xsl:template> 

</xsl:stylesheet> 

В качестве примера он выводит таблицу HTML - ее необходимо легко адаптировать для других выходов.