2010-10-29 6 views
3

У меня есть комки сгенерированного HTML делать глупые вещи, как это:Объединение последовательностей элементов в XSLT

<p>Hey it's <em>italic</em><em>italic</em>!</p> 

И я хотел бы, чтобы размять, что вплоть до:

<p>Hey it's <em>italicitalic</em>!</p> 

Мой первый попытка была вдоль этих линий ...

<xsl:template match="em/preceding::em"> 
    <xsl:value-of select="$OPEN_EM"/> 
    <xsl:apply-templates/> 
</xsl:template> 

<xsl:template match="em/following::em"> 
    <xsl:apply-templates/> 
    <xsl:value-of select="$CLOSE_EM"/> 
</xsl:template> 

Но, очевидно, XSLT спецификации в бабушкиной доброте запрещает использование ГНО ndard XPath preceding или following оси в шаблонах. (И это потребует некоторой настройки для обработки трех ems в строке в любом случае.)

Любые решения лучше, чем забывать об этом в XSLT и просто запускать в $ LANGUAGE_OF_CHOICE на конечный результат? Грубые требования: не следует комбинировать два <em>, если они разделены чем-либо (пробелы, текст, теги), и, хотя он не должен их объединять, он должен хотя бы создать допустимый XML, если в строке есть три или более <em> , Обработка тегов, вложенных в ems (включая другие ems), не требуется.

(И о, я видел how to merge element using xslt?, который похож, но не совсем то же XSLT 2 является, к сожалению, не вариант, а предлагаемые решения выглядят чудовищно сложным.).

+0

Хороший вопрос, +1. См. Мой ответ для краткого и полного решения XSLT 1.0. :) –

ответ

2

Это также как группировка adjacents:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:template match="node()|@*"> 
     <xsl:copy> 
      <xsl:apply-templates select="node()[1]|@*"/> 
     </xsl:copy> 
     <xsl:apply-templates select="following-sibling::node()[1]"/> 
    </xsl:template> 
    <xsl:template match="em"> 
     <em> 
      <xsl:call-template name="merge"/> 
     </em> 
     <xsl:apply-templates 
      select="following-sibling::node()[not(self::em)][1]"/> 
    </xsl:template> 
    <xsl:template match="node()" mode="merge"/> 
    <xsl:template match="em" name="merge" mode="merge" > 
     <xsl:apply-templates select="node()[1]"/> 
     <xsl:apply-templates select="following-sibling::node()[1]" 
          mode="merge"/> 
    </xsl:template> 
</xsl:stylesheet> 

Выход:

<p>Hey it's <em>italicitalic</em>!</p> 

Примечание: Fine правило graneid идентичности обхода (копировать все, узел на узле); em правило (всегда первое, потому что процесс является узлом по узлу), сотрясение и вызов merge шаблон, применить шаблон к следующему брату не em; em правило в merge режиме (также называемое merge), aplly шаблоны для первого ребенка (в этом случае это только текстовый узел, но это позволяет вложенные элементы), а затем следующего брата в режиме merge; правило «разбить», сопоставляя любую вещь, а не em (потому что тест имени превосходит проверку типа узла в приоритете) останавливает процесс.

+0

@Alejandro: Это очень короткое решение, но его трудно понять. Мне нужен отладчик, чтобы посмотреть, что происходит. Это особенно верно для комбинации двух последних шаблонов. –

+0

@ Dimitre: Как вы думаете? Это то же самое, что и раньше, для группировки примыканий. Скопируйте все, сначала соединитесь в группе, перейдите в открытый режим (обработайте всех братьев и сестер, остановитесь вне группы), обработайте следующий брат, а не в группе. – 2010-10-29 18:43:15

+0

@Alejandro: Было бы хорошо объяснить это в вашем ответе. Кроме того, тот факт, что последний шаблон переопределяет предыдущий шаблон, не слишком очевиден. Кроме того, название режима запутывает (по крайней мере, для меня). Лучшее имя будет «merge» или «merge-em». Чтобы сделать обработку очень понятной, я бы переписал пустой шаблон следующим образом: '' –

2

Это преобразование:

<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="kFollowing" 
    match="em[preceding-sibling::node()[1][self::em]]" 
    use="generate-id(preceding-sibling::node()[not(self::em)][1])"/> 

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

<xsl:template match= 
    "em[following-sibling::node()[1][self::em] 
     and 
     not(preceding-sibling::node()[1][self::em]) 
     ]"> 
    <em> 
    <xsl:apply-templates select= 
    "node() 
    | 
     key('kFollowing', 
      generate-id(preceding-sibling::node()[1]) 
     )/node()"/> 
    </em> 
</xsl:template> 
<xsl:template match= 
"em[preceding-sibling::node()[1][self::em]]"/> 
</xsl:stylesheet> 

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

<p>Hey it's <em>italic1</em><em>italic2</em><em>italic3</em>!</p> 

производит разыскиваемого, правильный результат:

<p>Hey it's <em>italic1italic2italic3</em>!</p> 

Обратите внимание:

  1. Применение правила в идентичности, чтобы скопировать каждый узел, как есть.

  2. Использование ключа для удобства использования следующих смежных элементов em.

  3. Первостепенной идентичности преобразование только для em элементов, которые имеют смежные em элементов.

  4. Это преобразование объединяет любое количество смежных em элементов.

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