Это преобразование XSLT 1.0 :
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="kTextById" match="text()" use="generate-id()"/>
<xsl:param name="pMaxLength" select="60"/>
<xsl:variable name="vTextToSplit">
<xsl:apply-templates select="(//text())[1]" mode="calc"/>
</xsl:variable>
<xsl:variable name="vsplitNode" select=
"key('kTextById', substring-before(substring-after($vTextToSplit,'|'), '|'))"/>
<xsl:variable name="vsplitLength" select=
"substring-before($vTextToSplit,'|')"/>
<xsl:variable name="vsplitPos" select=
"substring-after(substring-after($vTextToSplit,'|'),'|')"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:choose>
<xsl:when test="not($vTextToSplit)">
<xsl:copy-of select="."/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="/node()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="text()" mode="calc">
<xsl:param name="paccumLength" select="0"/>
<xsl:variable name="vPos" select="count(preceding::text())+1"/>
<xsl:variable name="vnewAccumLength" select=
"$paccumLength+string-length()"/>
<xsl:choose>
<xsl:when test="$vnewAccumLength >= $pMaxLength">
<xsl:value-of select=
"concat(string-length() - ($vnewAccumLength -$pMaxLength),
'|', generate-id(),
'|', $vPos
)"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates mode="calc"
select="(//text())[position() = $vPos+1]">
<xsl:with-param name="paccumLength" select="$vnewAccumLength"/>
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="text()">
<xsl:variable name="vPos" select="count(preceding::text())+1"/>
<xsl:choose>
<xsl:when test="$vPos > $vsplitPos"/>
<xsl:when test="$vPos = $vsplitPos">
<xsl:value-of select="substring(.,1,$vsplitLength)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
при применении к предоставленному входу (обернутый в один верхний элемент, чтобы сделать его правильно сформированным XML-документом):
<t>On the <b>approximate realization</b> of continuous mappings by <i>neural networks</i> <a href='http://...very long link'>some text</a>...</t>
дает требуемый, правильный результат - правильно сформированный XML-документ, содержащий элементы исходного XML-документа и общая длина текстовых узлов которого точно равна указанной длине (60) в глобальном параметре $pMaxLength
:
<t>On the <b>approximate realization</b> of continuous mappings by <i>neu</i>
<a href="http://...very long link"></a>
</t>
Объяснение
Глобальная переменная $vTextToSplit
рассчитывается . Это строка, содержащая три значения, разделенных по конвейеру: длина в «узле разделения», которая должна быть удалена, generate-id()
от «узла разделения» и порядковый номер «узла разделения» среди всех текстовые узлы, в порядке документа. «Узел разделения» - это текстовый узел, который содержит последний символ общей строки текстовых узлов, которые должны быть сгенерированы.
"Узел разбиения, его" generate-id()
и его длина, подлежащая обрезке, извлекаются из `$ vTextToSplit" в три отвечающие ядру глобальные переменные.
Шаблон, соответствующий корню (/
) документа, проверяет крайний случай, когда общая длина текстовых узлов меньше указанной требуемой длины . Если это так, полный XML-документ копируется в вывод. Если это не так, обработка продолжается путем применения шаблонов к дочерним узлам.
Правило идентификации копирует все узлы "как есть" .
Шаблон, соответствующий любому текстовому узлу, переопределяет шаблон идентификации . Он обрабатывает сопоставленный текстовый узел одним из трех способов: если этот текстовый узел имеет меньшую позицию, чем «разделенный узел», он копируется полностью. Если совпадающий узел имеет позицию больше, чем «разделенный узел», то его строковое значение не копируется. Наконец, если это сам узел разбиения, копируются все символы его строкового значения, за исключением завершающих символов $vsplitLength
.
II. Решение XSLT 2.0 :
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes"/>
<xsl:param name="pMaxLength" select="60"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match=
"text()[not(sum((.|preceding::text())/string-length(.))
gt
$pMaxLength)
]">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match=
"text()[sum(preceding::text()/string-length(.))
gt
$pMaxLength
]"/>
<xsl:template match=
"text()[sum((.|preceding::text())/string-length(.))
ge
$pMaxLength
and
not(sum(preceding::text()/string-length(.))
gt
$pMaxLength)
]">
<xsl:variable name="vprevLength" select=
"sum(preceding::text()/string-length(.))"/>
<xsl:variable name="vremainingLength" select=
"$pMaxLength - $vprevLength"/>
<xsl:copy-of select="substring(.,1,$vremainingLength)"/>
</xsl:template>
</xsl:stylesheet>
при применении к тому же исходному XML-документу (указанному выше), получается тот же правильный результат :
<t>On the <b>approximate realization</b> of continuous mappings by <i>neu</i><a href="http://...very long link"/></t>
Примечание по производительности : Оба представленных решения будут медленными для больших XML-документов. Один из способов избежать этого - использовать функцию / шаблон scanl()
из FXSL . Я предоставлю это третье решение позже, когда у меня будет больше свободного времени.