2012-05-14 3 views
1

У меня есть следующий XML:отделка смешанного содержимого к максимальному количеству символов с помощью XSLT

<p>Lorem ipsum dolor sit amet, <b>consectetur adipisicing</b> elit, <i>sed do<sup>2</sup></i> eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>

И я хочу показать только первые 200 символов, но он не может отрезать в середине слово, и я хочу сохранить элементы форматирования. Таким образом, выше фрагмента после преобразования становится:

<p>Lorem ipsum dolor sit amet, <b>consectetur adipisicing</b> elit, <i>sed do<sup>2</sup></i> eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ...</p>

Кто-нибудь знает, возможно ли это? Спасибо заранее!

+0

Это не вопрос XSLT. Наилучший подход - это обработать это в JavaScript/jQuey, накапливая длину текста в узлах-потомках, пока не найдете тот, который достигает вашего предела, а затем убивает остальных. –

+0

@torazaburo: На самом деле это можно добиться с помощью преобразования XSLT 2.0, и решение не так сложно. –

+0

Iddo: На самом деле слово, конечным символом которого является 200-й символ, является «ut». :) –

ответ

2

Это 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 omit-xml-declaration="yes" indent="no"/> 
<xsl:strip-space elements="*"/> 

<xsl:param name="pmaxChars" as="xs:integer" select="200"/> 

<xsl:variable name="vPass1"> 
    <xsl:apply-templates select="/*"/> 
</xsl:variable> 

<xsl:template match="node()|@*" mode="#default pass2"> 
    <xsl:copy> 
    <xsl:apply-templates select="node()|@*" mode="#current"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="/"> 
    <xsl:apply-templates select="$vPass1" mode="pass2"/> 
</xsl:template> 

<xsl:template match= 
"text()[sum(preceding::text()/string-length()) ge $pmaxChars]"/> 

<xsl:template match="text()[not(following::text())]" mode="pass2"> 
    <xsl:variable name="vPrecedingLength" 
    select="sum(preceding::text()/string-length())"/> 

    <xsl:variable name="vRemaininingLength" 
    select="$pmaxChars -$vPrecedingLength"/> 

    <xsl:sequence select= 
    "replace(., 
      concat('(^.{0,', $vRemaininingLength, '})\W.*'), 
      '$1' 
      ) 
    "/> 
</xsl:template> 
</xsl:stylesheet> 

при нанесении на прилагаемом документе XML:

<p>Lorem ipsum dolor sit amet, <b>consectetur adipisicing</b> elit, <i>sed do<sup>2</sup></i> eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p> 

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

<p>Lorem ipsum dolor sit amet, <b>consectetur adipisicing</b> elit, <i>sed do<sup>2</sup></i> eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut</p> 

Пояснение:

  1. Это общее решение, которое принимает максимальное количество текстовых символов в качестве глобального/внешнего параметра $pmaxChars ,

  2. Это двухпроходное решение. В pass1 identity rule переопределяется шаблоном, который удаляет все текстовые узлы, стартовый символ которых имеет индекс (в общей конкатенации всех текстовых узлов), больше максимального количества допустимых символов. Таким образом, результат pass1 - это XML-документ, в котором в последнем текстовом узле происходит «разрыв» на максимально допустимой длине.

  3. В pass 2 мы переопределяем правило идентификации шаблоном, который соответствует последнему текстовому узлу. Мы используем функцию replace():

....

replace(., 
      concat('(^.{0,', $vRemaininingLength, '})\W.*'), 
      '$1' 
      ) 

это вызывает полную строку к сопоставлению и должны быть заменены на подвыражения между скобками. Это подвыражение динамически построено и соответствует самой длинной подстроке, начинающейся с начала строки и содержащей от 0 до $vRemaininingLength (максимальная допустимая длина минус общая длина всех предшествующих текстовых узлов), а за ней сразу следует граница слов персонаж.

UPDATE:

Чтобы избавиться от результирующих элементов, которые из-за подрезки не имеют текстовый узел потомков (являются «пустыми»), просто добавьте этот дополнительный шаблон:

<xsl:template match= 
"*[(.//text())[1][sum(preceding::text()/string-length()) ge $pmaxChars]]"/> 
+0

Спасибо, Димитр! Очень умное решение! Я добавил следующее соответствие шаблона, чтобы избежать пустых тегов стиля: '' – iddo

+0

@ Иддо: Добро пожаловать. Что касается пустых элементов, в вашем вопросе вы не указали, что их нужно удалить, поэтому я думал, что оставить их в порядке. –

+0

@Iddo: Посмотрите обновление (в самом конце) на этот ответ. –

Смежные вопросы