2015-10-06 1 views
6

У меня есть документ HTML, который может содержать &lt; и &gt; в некоторых атрибутах. Я пытаюсь извлечь это и запустить его через XSLT, но ошибки XSLT-движка говорят мне, что < недопустим внутри атрибута.innerHTML unencodes < в атрибутах

Я сделал некоторые раскопки и обнаружил, что он правильно убежали в исходном документе, но при этом загружаются в DOM через innerHTML, то DOM является unencoding атрибутов. Странно, он делает это для &lt; и &gt;, но не некоторые другие, такие как &amp;.

Вот простой пример:

var div = document.createElement('DIV'); 
 
div.innerHTML = '<div asdf="&lt;50" fdsa="&amp;50"></div>'; 
 
console.log(div.innerHTML)

Я предполагаю, что реализация DOM решила, что HTML атрибуты могут быть менее строгими, чем атрибуты XML, и что это «работает, как предназначена". Мой вопрос: могу ли я обойти это без написания какой-нибудь ужасной замены регулярных выражений?

+0

@Abel Я использую '.html JQuery (в)', я просто пытался сократить вниз, туда, где я думаю, что «проблема» происходит. Исходным документом является XML, который я запускаю через браузер XSLT перед вставкой с помощью .html() '. Позже я прохожу через обратный процесс, чтобы вернуть XML. Мне просто кажется странным, что DOM * скрывает этот символ (а не другие). – murrayju

+0

Я не могу изменить исходный XML, и вам нужно сохранить тот же контент в конце в конце.Я мог запускать любые преобразования, необходимые в середине, но я ищу способ сделать это лучше, чем замена какого-либо регулярного выражения. Особенно учитывая, что персонаж '<', который заполнен документом. – murrayju

+0

@Abel моя единственная цель - вернуть его обратно из DOM так же, как и он (как '<'). Я помещаю его в '.text (string)' и вынимаю его с помощью .text() '. Проблема, с которой я сталкиваюсь в этом раунде, заключается в том, что вход не равен выходному (только в этом случае). – murrayju

ответ

0

То, что в итоге оказалось лучшим для меня, заключалось в том, чтобы удвоить их с помощью XSLT на входящем документе (и отменить это в исходящем документе).

Таким образом, &lt; в атрибуте становится &amp;lt;. Спасибо @Abel за предложение.

Вот XSLT добавил я, в случае, если другие считают его полезным:

Первый шаблон для выполнения строки замены в XSLT 1.0. Если вы можете использовать XSLT 2.0, вы можете использовать встроенный в replace.

<xsl:template name="string-replace-all"> 
    <xsl:param name="text"/> 
    <xsl:param name="replace"/> 
    <xsl:param name="by"/> 
    <xsl:choose> 
     <xsl:when test="contains($text, $replace)"> 
      <xsl:value-of select="substring-before($text,$replace)"/> 
      <xsl:value-of select="$by"/> 
      <xsl:call-template name="string-replace-all"> 
       <xsl:with-param name="text" select="substring-after($text,$replace)"/> 
       <xsl:with-param name="replace" select="$replace"/> 
       <xsl:with-param name="by" select="$by"/> 
      </xsl:call-template> 
     </xsl:when> 
     <xsl:otherwise> 
      <xsl:value-of select="$text"/> 
     </xsl:otherwise> 
    </xsl:choose> 
</xsl:template> 

Далее следуют шаблон, который делает определенные замены, которые мне нужны:

<!-- xml -> html --> 
<xsl:template name="replace-html-codes"> 
    <xsl:param name="text"/> 
    <xsl:variable name="lt"> 
     <xsl:call-template name="string-replace-all"> 
      <xsl:with-param name="text" select="$text"/> 
      <xsl:with-param name="replace" select="'&lt;'"/> 
      <xsl:with-param name="by" select="'&amp;lt;'"/> 
     </xsl:call-template> 
    </xsl:variable> 
    <xsl:variable name="gt"> 
     <xsl:call-template name="string-replace-all"> 
      <xsl:with-param name="text" select="$lt"/> 
      <xsl:with-param name="replace" select="'&gt;'"/> 
      <xsl:with-param name="by" select="'&amp;gt;'"/> 
     </xsl:call-template> 
    </xsl:variable> 
    <xsl:value-of select="$gt"/> 
</xsl:template> 

<!-- html -> xml --> 
<xsl:template name="restore-html-codes"> 
    <xsl:param name="text"/> 
    <xsl:variable name="lt"> 
     <xsl:call-template name="string-replace-all"> 
      <xsl:with-param name="text" select="$text"/> 
      <xsl:with-param name="replace" select="'&amp;lt;'"/> 
      <xsl:with-param name="by" select="'&lt;'"/> 
     </xsl:call-template> 
    </xsl:variable> 
    <xsl:variable name="gt"> 
     <xsl:call-template name="string-replace-all"> 
      <xsl:with-param name="text" select="$lt"/> 
      <xsl:with-param name="replace" select="'&amp;gt;'"/> 
      <xsl:with-param name="by" select="'&gt;'"/> 
     </xsl:call-template> 
    </xsl:variable> 
    <xsl:value-of select="$gt"/> 
</xsl:template> 

XSLT в основном сквозной. Я просто вызвать соответствующий шаблон, когда атрибуты копирования:

<xsl:template match="@*"> 
    <xsl:attribute name="data-{local-name()}"> 
     <xsl:call-template name="replace-html-codes"> 
      <xsl:with-param name="text" select="."/> 
     </xsl:call-template> 
    </xsl:attribute> 
</xsl:template> 

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

Я не уверен, что это то, что вы ищете, но посмотрите.

var div1 = document.createElement('DIV'); 
var div2 = document.createElement('DIV'); 
div1.setAttribute('asdf','&lt;50'); 
div1.setAttribute('fdsa','&amp;50'); 
div2.appendChild(div1); 
console.log(div2.innerHTML.replace(/&amp;/g, '&')); 
+0

Я не вижу, как это отвечает на вопрос с экранированными менее-то персонажами внутри атрибутов ... И вы, вероятно, не хотите, чтобы * каждый * амперсанд заменялся ... – Abel

+0

На самом деле он преобразует < и & в & lt; и & amp; соответственно. Функция замены меняет его на исходный формат. – Sumesh

+0

Точно мое мнение. '&' не следует заменять, '<' следует заменять только в том случае, если он является частью значения свойства, как если бы строка была интерпретирована как XML. Он не должен заменять другие вхождения (текстовые узлы, узлы комментариев, инструкции обработки, разделы cdata, хотя некоторые из них встречаются редко в HTML). – Abel

2

Попробуйте XMLSerializer:

var div = document.getElementById('d1'); 
 

 
var pre = document.createElement('pre'); 
 
pre.textContent = div.outerHTML; 
 
document.body.appendChild(pre); 
 

 
pre = document.createElement('pre'); 
 
pre.textContent = new XMLSerializer().serializeToString(div); 
 
document.body.appendChild(pre);
<div id="d1" data-foo="a &lt; b &amp;&amp; b &gt; c">This is a test</div>

Вы, возможно, потребуется адаптировать XSLT принять во внимание пространство имен XMLSerializer вставок XHTML (по крайней мере здесь, в тесте с Firefox).

+0

Это ближе к тому, что я хочу, но он не работает во всех браузерах (IE8 не имеет 'XMLSerializer') – murrayju

+0

@murrayju, [см. Этот вопрос в XML-сериализаторе] (http://stackoverflow.com/questions/4916327/javascript-replacement-for-xmlserializer-serializetostring), если вам нужно поддерживать (старые) браузеры с <3% -ным долей пользователей, вы можете, и в этом случае просто используйте '.xml'. Я думаю, что это решение Мартина Хоннен превосходно :). – Abel

+0

@Abel, я не думаю, что свойство 'xml' реализовано в IE или в другом месте для узлов HTML DOM, оно существует только для узлов DOM MSXML. –