Разделить строку на основе перевода строки и отобразить на ограниченное количество элементов - PullRequest
5 голосов
/ 05 марта 2011

У меня есть вопрос.

Мой входной XML выглядит как

<?xml version="1.0" encoding="UTF-8"?>
<Text>
    <Message>this is line 1
    this is line 2
    this is line 3
    this is line 4
    this is line 5
    this is line 6
    this is line 7
    ....
    ....n
    </Message>
</Text>

Содержимое в сообщении разделено переводом строки или переводом каретки, а номер строки неопределен.

Вывод будет:

<?xml version="1.0" encoding="UTF-8" ?> 
<Text>
  <Line>this is line 1</Line> 
  <Line>this is line 2</Line> 
  <Line>this is line 3</Line> 
  <Line>this is line 4</Line> 
  <Line>this is line 5</Line> 
  <Line>this is line 6</Line> 
  <Line>this is line 7</Line> 
  <Line>this is line 8</Line> 
  <Line>this is line 9</Line> 
  <Line>this is line 10</Line> 
</Text>

Я написал следующий XSL:

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

    <xsl:variable name="lineFeed"><xsl:text>&#xA;</xsl:text></xsl:variable>
    <xsl:variable name="carriageReturn"><xsl:text>&#xD;</xsl:text></xsl:variable>
    <xsl:template match="/">
    <Text>
           <xsl:if test="Text/Message">
                <xsl:choose>
                    <xsl:when test="contains(Text/Message, $lineFeed)">
                        <xsl:call-template name="TextWithLineBreaks">
                            <xsl:with-param name="string" select="Text/Message"/>
                            <xsl:with-param name="delimiter" select="$lineFeed"/>
                        </xsl:call-template>
                    </xsl:when>
                    <xsl:when test="contains(Text/Message, $carriageReturn)">
                        <xsl:call-template name="TextWithLineBreaks">
                            <xsl:with-param name="string" select="Text/Message"/>
                            <xsl:with-param name="delimiter" select="$carriageReturn"/>
                        </xsl:call-template>
                    </xsl:when>
                    <xsl:otherwise>
                        <Line>
                            <xsl:value-of select="Text/Message"/>
                        </Line>
                    </xsl:otherwise>
                 </xsl:choose>
             </xsl:if>
    </Text>
    </xsl:template>

    <xsl:template name="TextWithLineBreaks">
        <xsl:param name="string"/>
        <xsl:param name="delimiter"/>
        <xsl:variable name="Result">
            <xsl:call-template name="extract-bodytext">
                <xsl:with-param name="GetString" select="$string"/>
                <xsl:with-param name="Separator" select="$delimiter"/>
            </xsl:call-template>
        </xsl:variable>
        <xsl:copy-of select="$Result"/>
    </xsl:template>

    <xsl:template name="extract-bodytext">
        <xsl:param name="GetString"/>
        <xsl:param name="Separator"/>
        <xsl:choose>
            <xsl:when test="contains($GetString, $Separator)">
                <xsl:variable name="firstline" select="substring-before($GetString, $Separator)"/>
                <xsl:if test="string-length($firstline) > 0">
                    <Line>
                        <xsl:value-of select="substring-before($GetString, $Separator)"/>
                    </Line>
                </xsl:if>
                <xsl:call-template name="extract-bodytext">
                    <xsl:with-param name="GetString">
                        <xsl:value-of select="substring-after($GetString,$Separator)"/>
                    </xsl:with-param>
                    <xsl:with-param name="Separator" select="$lineFeed"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$GetString"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>             
</xsl:stylesheet>

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

Есть предложения?

Спасибо, Дин

Ответы [ 4 ]

4 голосов
/ 05 марта 2011

Это один из способов сделать это:

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

 <xsl:template match="/*">
  <Text>
    <xsl:apply-templates/>
  </Text>
 </xsl:template>

 <xsl:template match="text()" name="wrapLines">
  <xsl:param name="pText" select="."/>
  <xsl:param name="pNumLines" select="10"/>

  <xsl:if test=
   "string-length($pText) and $pNumLines > 0">
   <xsl:variable name="vLine" select=
   "substring-before(concat($pText,'&#xA;'), '&#xA;')"/>
   <Line>
    <xsl:value-of select="$vLine"/>
   </Line>

   <xsl:call-template name="wrapLines">
    <xsl:with-param name="pNumLines" select="$pNumLines -1"/>
    <xsl:with-param name="pText" select=
     "substring-after($pText, '&#xA;')"/>
   </xsl:call-template>
  </xsl:if>
 </xsl:template>
</xsl:stylesheet>

, когда это преобразование применяется к следующему документу XML (содержащему более 10 строк):

<Text>
    <Message>this is line 1
    this is line 2
    this is line 3
    this is line 4
    this is line 5
    this is line 6
    this is line 7
    this is line 8
    this is line 9
    this is line 10
    this is line 11
    </Message>
</Text>

желаемый, правильный результат:

<Text>
   <Line>this is line 1</Line>
   <Line>   this is line 2</Line>
   <Line>   this is line 3</Line>
   <Line>   this is line 4</Line>
   <Line>   this is line 5</Line>
   <Line>   this is line 6</Line>
   <Line>   this is line 7</Line>
   <Line>   this is line 8</Line>
   <Line>   this is line 9</Line>
   <Line>   this is line 10</Line>
</Text>

Решение 2 :

Используя str-split-to-words шаблон / функцию FXSL, можно просто написать :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common"
 exclude-result-prefixes="ext"
>
  <xsl:import href="strSplit-to-Words.xsl"/>
  <xsl:output indent="yes" omit-xml-declaration="yes"/>

   <xsl:strip-space elements="*"/>
   <xsl:output indent="yes" omit-xml-declaration="yes"/>

   <xsl:param name="pmaxLines" select="10"/>

    <xsl:template match="/">
     <Text>
      <xsl:variable name="vwordNodes">
        <xsl:call-template name="str-split-to-words">
          <xsl:with-param name="pStr" select="/"/>
          <xsl:with-param name="pDelimiters"
                          select="'&#10;&#13;'"/>
        </xsl:call-template>
      </xsl:variable>

      <xsl:apply-templates select=
       "ext:node-set($vwordNodes)/*[not(position() > $pmaxLines)]"/>
     </Text>
    </xsl:template>

    <xsl:template match="word">
      <Line><xsl:value-of select="."/></Line>
    </xsl:template>
</xsl:stylesheet>

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

<Text>
   <Line>this is line 1</Line>
   <Line>   this is line 2</Line>
   <Line>   this is line 3</Line>
   <Line>   this is line 4</Line>
   <Line>   this is line 5</Line>
   <Line>   this is line 6</Line>
   <Line>   this is line 7</Line>
   <Line>   this is line 8</Line>
   <Line>   this is line 9</Line>
   <Line>   this is line 10</Line>
</Text>
1 голос
/ 05 марта 2011

Вы пытались использовать position() или <xsl:number/>? Любой из них может быть способом сделать то, что вы хотите.


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

0 голосов
/ 05 марта 2011

Я немного изменил ваш XSL и получил первые 10 строк:

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

    <xsl:template match="/">
    <xsl:variable name="all">
    <Text>
      unchanged generation of all the lines in the Text list
    </Text>
    </xsl:variable>
    <Text>
    <xsl:copy-of select="$all/Text/Line[position() le 10]"/>
    </Text>
    </xsl:template>

    rest unchanged

</xsl:stylesheet>

Конечно, это клочок, потому что если у вас есть тысячи строк, из которых вы хотите только первые десять, тогда это пустая трата ... ОТО, если вы знаете, что количество строк разумное, это дешево.

Кроме того, это не будет работать в XSLT 1.0 / Xpath 1.0 без использования расширения exslt: node-set.

0 голосов
/ 05 марта 2011

Я бы перевел () все окончания строки в один символ, чтобы вы не в конечном итоге вызывали оба варианта $ lineFeed и $ carriageReturn, а только один шаблон вызова $ newline. Затем создайте переменную-счетчик, равную 10, и уменьшайте ее каждый раз, когда вы вызываете рекурсивный шаблон. проверить на соответствие исходному условию ИЛИ переменная = 0, поэтому будет завершаться всякий раз, когда у вас закончатся строки или процесс 10, в зависимости от того, что меньше.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...