如何创建包含JS的JSoup文档?

时间:2015-08-03 14:29:30

标签: javascript java jsoup

我创建了一个包含两个<script type="application/javascript">/* .. */</script>元素的JSoup文档。

问题:当我致电.html().toString()时,JSoup将逃脱我的JavaScript。

if (foo && bar) 

if (foo &amp;&amp; bar)

是否可以将JSoup配置为在转义时忽略<script>元素?

这是(基本上)我如何创建我的jsoup文档。

final Document doc = Document.createShell("");
final Element head = doc.head();
head.appendElement("meta").attr("charset", "utf-8");
final String myJS = ...;
head.appendElement("script").attr("type", "application/javascript").text(myJS);

我目前的解决方法是在String.replace上用.html()替换占位符。但这有点像黑客。

head.appendElement("script").attr("type", "application/javascript").text("$MYJS$");
String s = doc.html();
s = s.replace("$MYJS$", myJS);

2 个答案:

答案 0 :(得分:4)

无法再覆盖outerHtmlHead方法。

这就是我使用的:

head
    .appendElement("script")
    .attr("type","text/javascript")
    .appendChild(new DataNode(getJavaScript(),""));

答案 1 :(得分:-1)

你“无法”禁用它。通过“不能”我的意思是不容易。您必须拦截/重新实现/覆盖节点遍历,这是我认为太多了。你可以这样做

String dom = Parser.unescapeEntities(doc.html(), false);

<强>更新

首先,我们必须确定问题的位置。

<强> TextNode.java

void outerHtmlHead(StringBuilder accum, int depth,
        Document.OutputSettings out) {
    if ((out.prettyPrint())
            && ((((siblingIndex() == 0)
                    && (this.parentNode instanceof Element)
                    && (((Element) this.parentNode).tag().formatAsBlock()) && (!(isBlank()))) || ((out
                    .outline()) && (siblingNodes().size() > 0) && (!(isBlank())))))) {
        indent(accum, depth, out);
    }

    boolean normaliseWhite = (out.prettyPrint())
            && (parent() instanceof Element)
            && (!(Element.preserveWhitespace(parent())));
    Entities.escape(accum, getWholeText(), out, false, normaliseWhite,
            false);
}

特别是问题在这里

Entities.escape(accum, getWholeText(), out, false, normaliseWhite,false);

<强> Entities.java

static void escape(StringBuilder accum, String string,
        Document.OutputSettings out, boolean inAttribute,
        boolean normaliseWhite, boolean stripLeadingWhite) {
    boolean lastWasWhite = false;
    boolean reachedNonWhite = false;
    EscapeMode escapeMode = out.escapeMode();
    CharsetEncoder encoder = out.encoder();
    CoreCharset coreCharset = CoreCharset.access$300(encoder.charset()
            .name());
    Map map = escapeMode.getMap();
    int length = string.length();
    int codePoint;
    for (int offset = 0; offset < length; offset += Character
            .charCount(codePoint)) {
        codePoint = string.codePointAt(offset);

        if (normaliseWhite) {
            if (StringUtil.isWhitespace(codePoint)) {
                if ((stripLeadingWhite) && (!(reachedNonWhite)))
                    continue;
                if (lastWasWhite)
                    continue;
                accum.append(' ');
                lastWasWhite = true;
                continue;
            }
            lastWasWhite = false;
            reachedNonWhite = true;
        }

        if (codePoint < 65536) {
            char c = (char) codePoint;

            switch (c) {
            case '&':
                accum.append("&amp;");
                break;
            case ' ':
                if (escapeMode != EscapeMode.xhtml)
                    accum.append("&nbsp;");
                else
                    accum.append("&#xa0;");
                break;
            case '<':
                if ((!(inAttribute)) || (escapeMode == EscapeMode.xhtml))
                    accum.append("&lt;");
                else
                    accum.append(c);
                break;
            case '>':
                if (!(inAttribute))
                    accum.append("&gt;");
                else
                    accum.append(c);
                break;
            case '"':
                if (inAttribute)
                    accum.append("&quot;");
                else
                    accum.append(c);
                break;
            default:
                if (canEncode(coreCharset, c, encoder))
                    accum.append(c);
                else if (map.containsKey(Character.valueOf(c)))
                    accum.append('&')
                            .append((String) map.get(Character.valueOf(c)))
                            .append(';');
                else
                    accum.append("&#x")
                            .append(Integer.toHexString(codePoint))
                            .append(';');
            }
        } else {
            String c = new String(Character.toChars(codePoint));
            if (encoder.canEncode(c))
                accum.append(c);
            else
                accum.append("&#x").append(Integer.toHexString(codePoint))
                        .append(';');
        }
    }
}

好了,现在我们已经确定了问题,那么解决方案是什么。那么这就是问题所在。通常你要做的是覆盖outerHtmlHead(在调用html()toString()-calls outerHtmlouterHtml()时为每个节点调用。问题是这个方法是{{ 3}}所以它不可见,以便在包外面覆盖它。

一种简单的方法是下载Jsoup的源代码,并在同一个包中包含您的自定义类。另一种方法是将这两者的可见性改为受保护。

abstract void outerHtmlHead(StringBuilder paramStringBuilder, int paramInt, Document.OutputSettings paramOutputSettings);

abstract void outerHtmlTail(StringBuilder paramStringBuilder, int paramInt,Document.OutputSettings paramOutputSettings);

项目将在扩展class Node的每个类中都有编译错误,因为无法降低重写方法的可见性。 将可见性也更改为protected。 之后,您可以实现扩展TextNode类的新类。这样的事情就足够了我相信

public class RawTextNode extends TextNode {

    @Override
    protected void outerHtmlHead(StringBuilder accum, int depth, OutputSettings out) {
        if ((out.prettyPrint())
                && ((((siblingIndex() == 0)
                        && (parentNode() instanceof Element)
                        && (((Element) parentNode()).tag().formatAsBlock()) && (!(isBlank()))) || ((out
                        .outline()) && (siblingNodes().size() > 0) && (!(isBlank())))))) {
            indent(accum, depth, out);
        }
    }
}

并且您的代码必须相应更改

head.appendElement("script").attr("type", "application/javascript").appendChild(new RawTextNode(myJS, ""));

如果保持原样,文本将由TextNode表示,您需要明确说明文本必须由您的自定义类表示。

当然,您可以更深入地创建一个新类,以通用的方式处理script部分。