避免使用递归xslt模板

时间:2015-05-01 18:30:05

标签: xslt recursion

以下脚本说明了 Chrome 42.0.2311.135 在尝试将递归模板escapeStringForJson应用于长度超过一定长度的字符串(1874个字符)时的静默失败。我把它作为一个bug提交了,但是有没有办法重写模板以便它不是递归的?

    var transformXml = function (xml, xslt) {
      var xmlScrapeResult = (new DOMParser()).parseFromString(xml, 'text/xml');
      var transform = (new DOMParser()).parseFromString(xslt, 'text/xml');
      var xsltProcessor = new XSLTProcessor();
      xsltProcessor.importStylesheet(transform);

      var transformed = xsltProcessor.transformToFragment(xmlScrapeResult, document);

      if (transformed != null) {
        return transformed;
      }
      else {
        throw 'xsltProcessor.transformToFragment() unexpectedly returned null';
      }
    }

    var xslt = '<?xml version="1.0"?>\
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">\
    <xsl:preserve-space elements="*" />\
    <xsl:template match="/MyRoot">\
    {\n\
      "escapedDoubleQuotesAndNewlines": "<xsl:call-template name="escapeStringForJson"><xsl:with-param name="text" select="MyElement"/></xsl:call-template>"\n\
    }\
    </xsl:template>\
    <xsl:template name="escapeStringForJson">\
      <xsl:param name="text"/>\
      <xsl:if test="$text != \'\'">\
        <xsl:variable name="head" select="substring($text, 1, 1)"/>\
        <xsl:variable name="tail" select="substring($text, 2)"/>\
        <xsl:variable name="apos">\'</xsl:variable>\
        <xsl:variable name="quot">"</xsl:variable>\
        <xsl:variable name="nl"><xsl:text>&#10;</xsl:text></xsl:variable>\
        <xsl:variable name="cr"><xsl:text>&#13;</xsl:text></xsl:variable>\
        <xsl:variable name="sl">\</xsl:variable>\
        <xsl:choose>\
          <!--<xsl:when test="$head = $apos">\\\'</xsl:when>-->\
          <xsl:when test="$head = $quot">\\"</xsl:when>\
          <xsl:when test="$head = $nl">\\n</xsl:when>\
          <xsl:when test="$head = $cr">\\r</xsl:when>\
          <xsl:when test="$head = $sl">\\\\</xsl:when>\
          <xsl:otherwise><xsl:value-of select="$head"/></xsl:otherwise>\
        </xsl:choose>\
        <xsl:call-template name="escapeStringForJson">\
          <xsl:with-param name="text" select="$tail"/>\
        </xsl:call-template>\
      </xsl:if>\
    </xsl:template>\
    </xsl:stylesheet>';

    var works1 = '<?xml version="1.0"?>\
    <MyRoot>\
      <MyElement>This is "test 1" illustrating escapeStringForJson purpose.</MyElement>\
    </MyRoot>';

    var works2 = '<?xml version="1.0"?>\
    <MyRoot>\
      <MyElement>' +
    '0123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 12345 100' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 12345 200' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 12345 300' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 12345 400' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 12345 500' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 12345 600' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 12345 700' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 12345 800' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 12345 900' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 1234 1000' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 1234 1100' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 1234 1200' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 1234 1300' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 1234 1400' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 1234 1500' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 1234 1600' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 1234 1700' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 1234 1800' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 12345678 1874' +
    '</MyElement>\
    </MyRoot>';

    var nowork = '<?xml version="1.0"?>\
    <MyRoot>\
      <MyElement>' +
    '0123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 12345 100' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 12345 200' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 12345 300' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 12345 400' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 12345 500' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 12345 600' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 12345 700' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 12345 800' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 12345 900' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 1234 1000' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 1234 1100' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 1234 1200' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 1234 1300' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 1234 1400' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 1234 1500' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 1234 1600' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 1234 1700' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 1234 1800' +
    ' 123456789 123456789 123456789 123456789 123456789 123456789 123456789 1875' +
    '</MyElement>\
    </MyRoot>';

    console.log(new XMLSerializer().serializeToString(transformXml(works1, xslt)));
    console.log(new XMLSerializer().serializeToString(transformXml(works2, xslt))); // these two length-related test-cases do not have any characters that need escaping
    console.log(new XMLSerializer().serializeToString(transformXml(nowork, xslt))); // these two length-related test-cases do not have any characters that need escaping
(See your developer console output)

1 个答案:

答案 0 :(得分:0)

感谢@ michael.hor257k,我想出了这些。

这将字符串拆分为它找到的第一个"(或其他搜索到的字符,按顺序)到beforeafter,并递归调用两半的模板。 before将没有",因为我们已经在调用步骤中找到的第一个分割,因此它只能按顺序匹配后面的字符。因此,before分支肯定会取得进展,因为它耗尽了所搜索的字符列表。 after分支可以找到另一个相同的字符,但肯定会沿着字符串的长度前进,始终至少前进一个字符。

<xsl:template name="escapeForJson">
    <xsl:param name="text"/>
    <xsl:variable name="quot">
        <xsl:text>"</xsl:text>
    </xsl:variable>
    <xsl:variable name="nl">
        <xsl:text>&#10;</xsl:text>
    </xsl:variable>
    <xsl:variable name="cr">
        <xsl:text>&#13;</xsl:text>
    </xsl:variable>
    <xsl:variable name="sl">
        <xsl:text>\</xsl:text>
    </xsl:variable>
    <xsl:choose>
        <xsl:when test="contains($text, $quot)">
            <xsl:call-template name="escapeForJson">
                <xsl:with-param name="text" select="substring-before($text,$quot)"/>
            </xsl:call-template>
            <xsl:text>\"</xsl:text>
            <xsl:call-template name="escapeForJson">
                <xsl:with-param name="text" select="substring-after($text,$quot)"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:when test="contains($text, $nl)">
            <xsl:call-template name="escapeForJson">
                <xsl:with-param name="text" select="substring-before($text,$nl)"/>
            </xsl:call-template>
            <xsl:text>\n</xsl:text>
            <xsl:call-template name="escapeForJson">
                <xsl:with-param name="text" select="substring-after($text,$nl)"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:when test="contains($text, $cr)">
            <xsl:call-template name="escapeForJson">
                <xsl:with-param name="text" select="substring-before($text,$cr)"/>
            </xsl:call-template>
            <xsl:text>\r</xsl:text>
            <xsl:call-template name="escapeForJson">
                <xsl:with-param name="text" select="substring-after($text,$cr)"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:when test="contains($text, $sl)">
            <xsl:call-template name="escapeForJson">
                <xsl:with-param name="text" select="substring-before($text,$sl)"/>
            </xsl:call-template>
            <xsl:text>\\</xsl:text>
            <xsl:call-template name="escapeForJson">
                <xsl:with-param name="text" select="substring-after($text,$sl)"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$text" disable-output-escaping="yes"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>