2014-06-05 21 views
5

Używam jsoup 1.7.3 z niestandardową konfiguracją Whitelist.Jsoup WhiteList, aby umożliwić komentarze

Najwyraźniej oczyszcza on wszystkie komentarze HTML (<!-- ... -->) wewnątrz dokumentu.

Umożliwia także odkażanie elementu <!DOCTYPE ...>.

  1. Jak mogę uzyskać jsoup Whitelist, aby umożliwić komentowanie w niezmienionej postaci?
  2. Jak zdefiniować element !DOCTYPE jako dozwolony element z dowolnym atrybutem?

Odpowiedz

3

Nie jest to możliwe przez standardowe klasy JSoup i nie jest zależne od WhiteList. Jest to org.jsoup.safety.Cleaner. Środek czyszczący korzysta z przeszukiwacza węzłów, który dopuszcza tylko elementy i węzły tekstowe. Analizowane jest również tylko ciało. Więc głowa i doctype są całkowicie ignorowane. Aby to osiągnąć, musisz stworzyć niestandardowy środek czyszczący. Na przykład, jeśli masz html taki jak

Najpierw utworzymy niestandardowy element czyszczący, który skopiuje oryginalny. Należy jednak pamiętać, że pakiet powinien być zgodny z org.jsoup.safety, ponieważ środek czyszczący korzysta z niektórych chronionych metod z Białą Listą. Nie ma również sensu rozszerzanie Cleanera, ponieważ prawie wszystkie metody są prywatne, a traverser węzła wewnętrznego jest ostateczny.

package org.jsoup.safety; 

import org.jsoup.helper.Validate; 
import org.jsoup.nodes.Attribute; 
import org.jsoup.nodes.Attributes; 
import org.jsoup.nodes.Comment; 
import org.jsoup.nodes.DataNode; 
import org.jsoup.nodes.Document; 
import org.jsoup.nodes.DocumentType; 
import org.jsoup.nodes.Element; 
import org.jsoup.nodes.Node; 
import org.jsoup.nodes.TextNode; 
import org.jsoup.parser.Tag; 
import org.jsoup.select.NodeTraversor; 
import org.jsoup.select.NodeVisitor; 

public class CustomCleaner { 
    private Whitelist whitelist; 

    public CustomCleaner(Whitelist whitelist) { 
    Validate.notNull(whitelist); 
    this.whitelist = whitelist; 
    } 

    public Document clean(Document dirtyDocument) { 
    Validate.notNull(dirtyDocument); 

    Document clean = Document.createShell(dirtyDocument.baseUri()); 
    copyDocType(dirtyDocument, clean); 
    if (dirtyDocument.head() != null) 
     copySafeNodes(dirtyDocument.head(), clean.head()); 
    if (dirtyDocument.body() != null) // frameset documents won't have a body. the clean doc will have empty body. 
     copySafeNodes(dirtyDocument.body(), clean.body()); 

    return clean; 
    } 

    private void copyDocType(Document dirtyDocument, Document clean) { 
    dirtyDocument.traverse(new NodeVisitor() { 
     public void head(Node node, int depth) { 
     if (node instanceof DocumentType) { 
      clean.prependChild(node); 
     } 
     } 
     public void tail(Node node, int depth) { } 
    }); 
    } 

    public boolean isValid(Document dirtyDocument) { 
    Validate.notNull(dirtyDocument); 

    Document clean = Document.createShell(dirtyDocument.baseUri()); 
    int numDiscarded = copySafeNodes(dirtyDocument.body(), clean.body()); 
    return numDiscarded == 0; 
    } 

    private final class CleaningVisitor implements NodeVisitor { 
    private int numDiscarded = 0; 
    private final Element root; 
    private Element destination; // current element to append nodes to 

    private CleaningVisitor(Element root, Element destination) { 
     this.root = root; 
     this.destination = destination; 
    } 

    public void head(Node source, int depth) { 
     if (source instanceof Element) { 
     Element sourceEl = (Element) source; 

     if (whitelist.isSafeTag(sourceEl.tagName())) { // safe, clone and copy safe attrs 
      ElementMeta meta = createSafeElement(sourceEl); 
      Element destChild = meta.el; 
      destination.appendChild(destChild); 

      numDiscarded += meta.numAttribsDiscarded; 
      destination = destChild; 
     } else if (source != root) { // not a safe tag, so don't add. don't count root against discarded. 
      numDiscarded++; 
     } 
     } else if (source instanceof TextNode) { 
     TextNode sourceText = (TextNode) source; 
     TextNode destText = new TextNode(sourceText.getWholeText(), source.baseUri()); 
     destination.appendChild(destText); 
     } else if (source instanceof Comment) { 
     Comment sourceComment = (Comment) source; 
     Comment destComment = new Comment(sourceComment.getData(), source.baseUri()); 
     destination.appendChild(destComment); 
     } else if (source instanceof DataNode) { 
     DataNode sourceData = (DataNode) source; 
     DataNode destData = new DataNode(sourceData.getWholeData(), source.baseUri()); 
     destination.appendChild(destData); 
     } else { // else, we don't care about comments, xml proc instructions, etc 
     numDiscarded++; 
     } 
    } 

    public void tail(Node source, int depth) { 
     if (source instanceof Element && whitelist.isSafeTag(source.nodeName())) { 
     destination = destination.parent(); // would have descended, so pop destination stack 
     } 
    } 
    } 

    private int copySafeNodes(Element source, Element dest) { 
    CleaningVisitor cleaningVisitor = new CleaningVisitor(source, dest); 
    NodeTraversor traversor = new NodeTraversor(cleaningVisitor); 
    traversor.traverse(source); 
    return cleaningVisitor.numDiscarded; 
    } 

    private ElementMeta createSafeElement(Element sourceEl) { 
    String sourceTag = sourceEl.tagName(); 
    Attributes destAttrs = new Attributes(); 
    Element dest = new Element(Tag.valueOf(sourceTag), sourceEl.baseUri(), destAttrs); 
    int numDiscarded = 0; 

    Attributes sourceAttrs = sourceEl.attributes(); 
    for (Attribute sourceAttr : sourceAttrs) { 
     if (whitelist.isSafeAttribute(sourceTag, sourceEl, sourceAttr)) 
     destAttrs.put(sourceAttr); 
     else 
     numDiscarded++; 
    } 
    Attributes enforcedAttrs = whitelist.getEnforcedAttributes(sourceTag); 
    destAttrs.addAll(enforcedAttrs); 

    return new ElementMeta(dest, numDiscarded); 
    } 

    private static class ElementMeta { 
    Element el; 
    int numAttribsDiscarded; 

    ElementMeta(Element el, int numAttribsDiscarded) { 
     this.el = el; 
     this.numAttribsDiscarded = numAttribsDiscarded; 
    } 
    } 

} 

Po wykonaniu obu czynności można wykonać czyszczenie w zwykły sposób. Jak

import java.io.File; 
import java.io.IOException; 

import org.jsoup.Jsoup; 
import org.jsoup.nodes.Document; 
import org.jsoup.safety.CustomCleaner; 
import org.jsoup.safety.Whitelist; 

public class CustomJsoupSanitizer { 

    public static void main(String[] args) { 
     try { 
      Document doc = Jsoup.parse(new File("t2.html"), "UTF-8"); 
      CustomCleaner cleaner = new CustomCleaner(Whitelist.relaxed().addTags("script")); 
      Document doc2 = cleaner.clean(doc); 
      System.out.println(doc2.html()); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 

    } 

} 

To daje oczyszczone wyjście powyżej html jak

<!DOCTYPE html> 
<html> 
<head> 
    <!-- This is a script --> 
    <script> 
     function newFun() { 
      alert(1); 
     } 
    </script> 
</head> 
<body>  
    <!-- This is another comment. --> 
    <div> 
    Test 
    </div> 
</body> 
</html> 

Można dostosować czystsze pasujące swoje wymagania. tj. aby uniknąć węzła głównego lub znacznika skryptu itp. ...

+0

To, czego się bałem. Dzięki. Szkoda, że ​​"rodzaj elementów do przeanalizowania" również można konfigurować. – Genry

0

Jsoup Cleaner nie daje Ci szansę tutaj (L 100).:

} else { // else, we don't care about comments, xml proc instructions, etc 
    numDiscarded++; 
} 

jedynie przypadki Element i TextNode może pozostać w oczyszczonym HTML.

Twoja jedyna szansa może być czymś okropnym, jak analizowanie dokumentu, zastępowanie komentarzy i doctype specjalnym znacznikiem na białej liście, czyszczenie dokumentu, a następnie przetwarzanie i zastępowanie specjalnych znaczników.

Powiązane problemy