將 xml 元素內容拆分為固定行數 (Split xml element content into fix number of lines)


問題描述

將 xml 元素內容拆分為固定行數 (Split xml element content into fix number of lines)

I would really appreciate a solution for this task that it's getting me crazy.

I have an xml element that has Free text inside, it could has 2 characters or a whole paragraph

  

<Description>text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text </Description>

I need a XSL template to produce this output:

11 lines with 80 charcters each.

For example for the above element, the result will be something like this:

I have been trying using a tokenize function, but no success at all. Thanks in advance.

Gerardo


參考解法

方法 1:

I. Here is an XSLT 1.0 solution (the XSLT 2.0 solution is much easier):

<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:variable name="vLine" select=
"concat('**********',
        '**********',
        '**********',
        '**********',
        '**********',
        '**********',
        '**********',
        '**********'
        )
"/>

 <xsl:template match="/*">
     <xsl:copy>
       <xsl:variable name="vsplitResult">
           <xsl:call-template name="split">
             <xsl:with-param name="pText" select="translate(., '&#xA;', '')"/>
           </xsl:call-template>
       </xsl:variable>

       <xsl:variable name="vnumLines" select=
       "ceiling(string-length($vsplitResult) div 81)"/>

       <xsl:choose>
        <xsl:when test="$vnumLines > 11">
          <xsl:value-of select="substring($vsplitResult, 1, 81*11 -1)"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:variable name="vremLines" select="11 - $vnumLines"/>

          <xsl:value-of select="substring($vsplitResult, 1, 81*($vnumLines -1))"/>

          <xsl:call-template name="padRight">
           <xsl:with-param name="pText" select="substring($vsplitResult,81*($vnumLines -1)+1)"/>
          </xsl:call-template>

          <xsl:for-each select="(document('')//node())[not(position() > $vremLines)]">
            <xsl:value-of select="concat('&#xA;', $vLine)"/>
          </xsl:for-each>
        </xsl:otherwise>
       </xsl:choose>
     </xsl:copy>
 </xsl:template>

 <xsl:template name="split">
  <xsl:param name="pText" select="."/>
  <xsl:param name="pLineLength" select="80"/>

  <xsl:if test="$pText">
   <xsl:value-of select="substring($pText, 1, $pLineLength)"/>
   <xsl:if test="string-length($pText) > $pLineLength">
    <xsl:text>&#xA;</xsl:text>
   </xsl:if>

   <xsl:call-template name="split">
     <xsl:with-param name="pText" select="substring($pText, $pLineLength+1)"/>
     <xsl:with-param name="pLineLength" select="$pLineLength"/>
   </xsl:call-template>
  </xsl:if>
 </xsl:template>

 <xsl:template name="padRight">
  <xsl:param name="pText"/>
  <xsl:param name="pTotatLength" select="80"/>

  <xsl:value-of select=
  "concat($pText,
          substring($vLine, 1, $pTotatLength - string-length($pText))
          )"/>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the provided XML document:

<Description>
text text text text text text text text text text text text text
text text text text text text text text text text text text text text text
text text text text </Description>

the wanted, correct result is produced (I am using the * character in order to see what exactly is generated):

<Description>text text text text text text text text text text text text texttext text text t
ext text text text text text text text text text text texttext text text text **
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************</Description>

II. XSLT 2.0 solution:

<xsl:stylesheet version="2.0"   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:param name="pLineLength" select="80" as="xs:integer"/>
 <xsl:param name="pTotalLines" select="11"  as="xs:integer"/>
 <xsl:param name="pPadChar" select="'*'" as="xs:string"/>

 <xsl:variable name="vLine" as="xs:string" select=
      "string-join(
                   (for $i in 1 to $pLineLength
                     return $pPadChar
                    ),
                   ''
                   )
     "/>
  <xsl:template match="/*">
   <xsl:variable name="vText" select="translate(., '&#xA;', '')"/>

   <xsl:copy>
     <xsl:value-of separator="&#xA;" select=
       "(for $numlines in string-length($vText) idiv $pLineLength +1,
             $line in 1 to $numlines
          return
             if($line ne $numlines)
               then substring($vText, 
                              1 + ($line -1)*$pLineLength, 
                              $pLineLength)
               else
                for $lastLine in substring($vText, 
                                           1 + ($line -1)*$pLineLength, 
                                           $pLineLength)
                 return
                   concat($lastLine, 
                          substring($vLine, 
                                    1, 
                                    $pLineLength - string-length($lastLine))
         ),

        (for $numlines in string-length($vText) idiv $pLineLength +1,
             $rem in 1 to $pTotalLines - $numlines
          return
             $vLine)
          )
       "/>
   </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the same XML document (above), the same correct result is produced:

<Description>text text text text text text text text text text text text texttext text text t
ext text text text text text text text text text text texttext text text text **
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************</Description>

方法 2:

Here's the final solution:

    ...
    <xsl:variable name="lineCount" select="11"/>
    <xsl:variable name="charPerLine" select="20"/>
    ...

<ElementX>
                        <xsl:call-template name="Texts">
                                    <xsl:with-param name="string" select="bi:fillText(../bi:SalesOrder/bi:Notes/bi:Description,$charPerLine*$lineCount)"/>
                                    <xsl:with-param name="Position">1</xsl:with-param>
                                    <xsl:with-param name="charLength" select="$charPerLine"/>
                                    <xsl:with-param name="lineCount" select="$lineCount"/>
                            </xsl:call-template>
                        </ElementX>

<!--Template to fill string. ..example 11 lines, 80 characters each.--> 
<xsl:template name="Texts">
    <xsl:param name="string"/>
    <xsl:param name="Position"/>
    <xsl:param  name="charLength" />
    <xsl:param name="lineCount"/>

    <xsl:variable name="line" select="substring($string,1,$charLength)"/>
    <xsl:variable name="rest" select="substring($string, $charLength+1)"/>
    <xsl:if test="$line">
        <xsl:value-of select="$line"/>
        <xsl:if test="$Position != $lineCount"> 
                <xsl:text>&#x0A;</xsl:text> 
        </xsl:if>
    </xsl:if>
    <xsl:if test="$rest and $Position &lt; $lineCount">
        <xsl:call-template name="Texts">
            <xsl:with-param name="string" select="$rest"/>
            <xsl:with-param name="Position" select="$Position+1"/>
            <xsl:with-param name="charLength"  select="$charLength"/>
            <xsl:with-param name="lineCount" select="$lineCount"/>
        </xsl:call-template>
    </xsl:if>
</xsl:template>

<xsl:function name="bi:fillText">
    <xsl:param name="value"/>
    <xsl:param name="lengthMax"/>
    <xsl:variable name="j" select="$lengthMax - string-length($value)"/>

    <xsl:value-of select="concat($value,string-join(
               (for $i in 1 to $j
                 return ' '
                ),
               ''
               ) )"/>
</xsl:function>

(by GerardoDimitre NovatchevGerardo)

參考文件

  1. Split xml element content into fix number of lines (CC BY-SA 3.0/4.0)

#split #xslt-2.0 #templates #xslt #xml






相關問題

將 xml 元素內容拆分為固定行數 (Split xml element content into fix number of lines)

是否有任何標准說明“aba”.split(/a/) 是否應該返回 1,2 或 3 個元素? (Is there any standard which says if "aba".split(/a/) should return 1,2, or 3 elements?)

Cố gắng gọi các phương thức trong phương thức main với biến được khởi tạo trong các phương thức khác (Trying to call methods in main method with variable initialized in other methods)

使用 Java-Regex 與 Regex 成對拆分多行文本 (Split text with Java-Regex in pairs with Regex over several lines)

如何分割字節數組 (How to split a byte array)

String componentsSeparatedByString 做一次 (String componentsSeparatedByString do one time)

從一行文本中獲取特定數據 (Get specific data from a line of text)

(Python)拆分字符串多個分隔符更有效?1) 使用多重替換方法然後使用拆分 2) 使用正則表達式 ((Python) which is more efficient to split a string multiple separators? 1) Using multiple replace method then using split 2) using regular Expressions)

ValueError:發現樣本數量不一致的輸入變量:[2935848、2935849] (ValueError: Found input variables with inconsistent numbers of samples: [2935848, 2935849])

在 Powershell 中拆分和添加字符串 (Splitting and Adding String in Powershell)

在 python 函數中檢查月份的有效性時出錯 (Error in checking validity of month in python function)

如何將 .obj 文件拆分為其他兩個文件(python、open3d)? (How to split a .obj file into two other files (python, open3d)?)







留言討論