問題描述
將 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(., '
', '')"/>
</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('
', $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>
</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(., '
', '')"/>
<xsl:copy>
<xsl:value-of separator="
" 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>
</xsl:text>
</xsl:if>
</xsl:if>
<xsl:if test="$rest and $Position < $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 Gerardo、Dimitre Novatchev、Gerardo)