2014-12-08 2 views
2

Я пытаюсь изменить ссылку внутри CDATA:Невозможно обработать элементы внутри CDATA

<paragraph> 
    <![CDATA[ 
     <strong><a href="http://example.com/2014/12/08/article-title">Article Title</a></strong>Article Excerpt. 
    ]]> 
</paragraph> 

Цель состоит в том, чтобы изменить пункт <p> и в то же время, добавить дополнительные теги внутри ссылки. Например, желаемый результат может быть: (не все <paragraph> есть ссылки в нем, некоторые содержат только текст)

<p> 
    <strong><a href="http://example.com/2014/12/08/article-title?tacking_id=12345" style="font-size:1.1em; color:#067ab4; line-height:100%">Article Title</a></strong>Article Excerpt. 
</p> 

Я попытался следующий код, но он не работает.

<xsl:template match="paragraph"> 
    <xsl:copy-of select="@*"/> 
    <xsl:text disable-output-escaping="yes"><![CDATA[<p>]]></xsl:text> 
    <xsl:value-of select="." disable-output-escaping="yes"/> 
    <xsl:text disable-output-escaping="yes"><![CDATA[</p>]]></xsl:text> 
</xsl:template> 

<xsl:template match="text()[contains(.,'&lt;a href=&#34;') and contains(.,'&#34;>')]"> 

    <xsl:variable name="link" select="substring-before(substring-after(., '&lt;a href=&#34;'), '&#34;>')"/> 

    <xsl:text disable-output-escaping="yes"><![CDATA[<a href="]]></xsl:text> 
    <xsl:value-of disable-output-escaping="yes" select="$link"/> 
    <xsl:text disable-output-escaping="yes"><![CDATA[&tracking_id=12345" ]]></xsl:text> 
    <xsl:value-of select="$link_style"/> 
    <xsl:text disable-output-escaping="yes"><![CDATA[>]]></xsl:text> 
    <xsl:apply-templates select="child::node()"/> 
    <xsl:text disable-output-escaping="yes"><![CDATA[</a>]]></xsl:text> 

</xsl:template> 
+0

Там * есть * нет элементов в CDATA - вот что это за! Предположим, вы прочитали строку из SQL в xml-файл, и одно из этих полей содержало HTML со всеми его объектами &wtf;, неиспользуемыми тегами
и другим недопустимым XML. Вы помещаете его в раздел CData, чтобы XML не пытался его разобрать. – Mike

ответ

4

Насколько процессор XML, то, CDATA в paragraph узел не содержит ссылок, или метки, или что-либо иное, чем один текстовый узел. Это всего лишь строка символов, поэтому вам нужно прибегнуть к некоторым хитроумным манипуляциям с строкой, если вы действительно хотели ее изменить.

Первая проблема заключается в том, что в шаблоне, соответствующем «абзацу», вы не делаете никаких xsl:apply-templates, поэтому ваш второй шаблон, который может соответствовать текстовому узлу под paragraph, никогда не вызывается.

Вы первый шаблон должен поэтому выглядеть следующим образом

<xsl:template match="paragraph"> 
    <p> 
     <xsl:apply-templates /> 
    </p> 
</xsl:template> 

Теперь в шаблоне сопоставления текстового узла, это где он получает неприятным, но ваша главная проблема в том, что вы делаете <xsl:apply-templates select="child::node()"/>. Но это текстовый узел. Один текстовый узел. У него нет дочерних узлов, с которыми вы можете сопоставлять.

Если вы действительно, действительно хотел, чтобы заставить его работать таким образом, шаблон будет выглядеть следующим образом

<xsl:template match="text()[contains(.,'&lt;a href=&#34;') and contains(.,'&#34;>')]"> 
    <xsl:variable name="firstbit" select="substring-before(., '&lt;a href=&#34;')"/> 
    <xsl:variable name="link" select="substring-before(substring-after(., '&lt;a href=&#34;'), '&#34;>')"/> 
    <xsl:variable name="lastbit" select="substring-after(substring-after(., '&lt;a href=&#34;'), '&#34;>')"/> 

    <xsl:value-of disable-output-escaping="yes" select="$firstbit"/> 
    <xsl:text disable-output-escaping="yes"><![CDATA[<a href="]]></xsl:text> 
    <xsl:value-of disable-output-escaping="yes" select="$link"/> 
    <xsl:text disable-output-escaping="yes"><![CDATA[?tracking_id=12345" ]]></xsl:text> 
    <xsl:value-of select="$link_style"/> 
    <xsl:text disable-output-escaping="yes"><![CDATA[>]]></xsl:text> 
    <xsl:value-of disable-output-escaping="yes" select="$lastbit"/> 
</xsl:template> 

Этот ответ, вероятно, демонстрирует, почему пытается манипулировать CDATA является плохой идеей.

Другим менее неприятным способом было бы сделать TWO XSLT-преобразования.

Первая будет просто смотреть, как этот

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
    <xsl:template match="paragraph"> 
     <p> 
      <xsl:value-of disable-output-escaping="yes" select="." /> 
     </p> 
    </xsl:template> 
</xsl:stylesheet> 

Это будет выводить следующий:

<p> 
    <strong><a href="http://example.com/2014/12/08/article-title">Article Title</a></strong>Article Excerpt. 
</p> 

Затем становится тривиальным сделать необходимые преобразования в a тегах в этом потом ... .

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
    <xsl:template match="@*|node()"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="a"> 
     <a href="{@href}?tracking_id=12345" style="color:#067ab4;"> 
      <xsl:apply-templates /> 
     </a> 
    </xsl:template> 
</xsl:stylesheet> 

Затем выдается следующее:

<p> 
    <strong><a href="http://example.com/2014/12/08/article-title?tracking_id=12345" style="color:#067ab4;">Article Title</a></strong>Article Excerpt. 
</p> 

Так что, если вы можете изменить входной XML для устранения CDATA, то это становится гораздо проще .....

+0

Привет, Тим, большое спасибо! Я попробую ваше первое решение.Проблема в том, что я не контролирую фид - я не могу удалить CDATA, поэтому пришлось найти противные способы работать с ним :( –

+0

Просто применил ваше решение к моему сценарию, отлично работает, огромное спасибо! –

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