2015-08-03 15 views
9

Tôi tạo một Tài liệu JSoup bao gồm hai phần tử <script type="application/javascript">/* .. */</script>.Cách tạo Tài liệu JSoup bao gồm JS?

Sự cố: Khi tôi gọi .html() hoặc .toString() JSoup sẽ thoát JavaScript của tôi.

if (foo && bar) 

được

if (foo &amp;&amp; bar) 

Có thể cấu hình JSoup bỏ qua <script> Elements khi thoát ??


Đây là (về cơ bản) cách tôi tạo tài liệu jsoup của mình.

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); 

workaround hiện tại của tôi là để thay thế một placeholder với String.replace trên .html(). Nhưng điều này là kinda hacky.

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

Tôi đã cập nhật câu trả lời của tôi. – alkis

Trả lời

-1

Bạn "không thể" tắt tính năng này. Bởi "không thể" tôi có nghĩa là không dễ dàng. Bạn sẽ phải đánh chặn/tái triển khai/ghi đè lên quá trình duyệt qua nút mà tôi nghĩ là quá nhiều. Bạn có thể làm điều này

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

Cập nhật

Trước hết chúng ta phải xác định vị trí của vấn đề.

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); 
} 

Đặc biệt vấn đề là ở đây

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(';'); 
     } 
    } 
} 

Ok bây giờ chúng tôi đã xác định được vấn đề, thì sao giải pháp. Đây là vấn đề. Thông thường bạn sẽ làm gì sẽ được ghi đè outerHtmlHead (được gọi là cho mỗi nút khi gọi html() hoặc toString()-calls outerHtml hoặc outerHtml(). Vấn đề là phương pháp này là package private vì vậy nó không thể nhìn thấy để ghi đè lên nó bên ngoài của gói.

Một cách dễ dàng sẽ được tải về mã nguồn của Jsoup và bao gồm lớp tùy chỉnh của bạn bên trong cùng một gói. Một sẽ phải thay đổi chế độ hiển thị của hai để được bảo vệ.

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

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

dự án sẽ có lỗi biên dịch trong mỗi lớp mở rộng class Node về việc không thể giảm e khả năng hiển thị của một phương pháp ghi đè. Thay đổi chế độ hiển thị thành protected. Sau đó bạn có thể triển khai một lớp mới mở rộng lớp TextNode.Một cái gì đó như thế này cũng đủ tôi tin

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); 
     } 
    } 
} 

và mã của bạn phải thay đổi cho phù

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

Nếu bạn để nó như nó là, văn bản sẽ được đại diện bởi TextNode, bạn cần phải nói rõ nội dung phải được đại diện bởi lớp tùy chỉnh của bạn.

Tất nhiên bạn có thể đi sâu hơn và tạo một lớp mới xử lý theo cách chung chung các bộ phận script.

+0

Nhưng điều đó cũng sẽ thoát khỏi HTML thực sự của tôi, đúng không? '>' to '>' etc – Brettetete

+0

Đúng. Tôi nghĩ rằng đây sẽ không phải là một vấn đề (mặc dù bạn đề cập rõ ràng rằng phần kịch bản phải trốn thoát). Lỗi của tôi. Tôi sẽ cố gắng cung cấp một cái gì đó khác sau này trong ngày. – alkis

+0

Tôi đã cập nhật câu trả lời của mình. Thật không may là giải pháp khá rắc rối. – alkis

2

Phương thức outerHtmlHead không còn bị ghi đè.

Đây là những gì tôi sử dụng:

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

Đây là những gì thực sự làm việc cho tôi! :) Cảm ơn bạn! – joaomgcd

Các vấn đề liên quan