本文目录导读:

针对 JEditorPane 和 HTMLEditorKit 在解析 HTML 时出现的 ParserError(解析器错误),通常是因为遇到了格式不规范的 HTML 或标签未正确关闭等问题,Swing 默认的 HTML 解析器(基于 Swing 自己的简易解析器,而非浏览器的完整解析器)对 HTML 的容错性较差,遇到错误可能会静默跳过或抛出异常。
以下是几种常见的错误处理及解决方案:
捕获并处理解析异常
使用 try-catch 包裹设置文本的操作:
import javax.swing.*;
import javax.swing.text.html.*;
import java.io.*;
public class HTMLErrorHandler {
public static void main(String[] args) {
JEditorPane editorPane = new JEditorPane();
editorPane.setContentType("text/html");
editorPane.setEditable(false);
// 可能包含错误 HTML 的字符串
String badHtml = "<html><body><p>Test<b>No close</p></html>";
try {
editorPane.setText(badHtml);
} catch (Exception e) {
System.err.println("HTML 解析错误: " + e.getMessage());
// 可执行降级策略,如显示纯文本
editorPane.setText(badHtml.replaceAll("<[^>]*>", ""));
}
}
}
自定义 HTMLEditorKit 的错误处理
通过继承 HTMLEditorKit 并覆盖 getParserCallback() 或使用 ParserDelegator 的 setErrorHandler 来捕获具体的解析错误。
示例:设置错误处理器
import javax.swing.text.html.*;
import javax.swing.text.html.parser.*;
import javax.swing.text.*;
public class CustomHTMLEditorKit extends HTMLEditorKit {
@Override
public ViewFactory getViewFactory() {
return new HTMLFactory() {
@Override
public View create(Element elem) {
return super.create(elem);
}
};
}
@Override
public Document createDefaultDocument() {
HTMLDocument doc = (HTMLDocument) super.createDefaultDocument();
// 设置解析器
ParserDelegator parser = new ParserDelegator() {
@Override
public void parse(Reader r, HTMLEditorKit.ParserCallback cb, boolean ignoreCharSet) throws IOException {
super.parse(r, new ErrorHandlingCallback(cb), ignoreCharSet);
}
};
// 通过反射或其他方式设置解析器
// 更推荐的方式:使用文档的 setParser 方法(需强制转换)
return doc;
}
// 内部回调类,包裹原始回调并捕获错误
private static class ErrorHandlingCallback extends HTMLEditorKit.ParserCallback {
private final HTMLEditorKit.ParserCallback delegate;
public ErrorHandlingCallback(HTMLEditorKit.ParserCallback delegate) {
this.delegate = delegate;
}
@Override
public void handleError(String errorMsg, int pos) {
System.err.println("解析错误 at position " + pos + ": " + errorMsg);
// 可选择忽略,继续解析
}
// 其他回调方法直接委托
@Override
public void handleText(char[] data, int pos) {
delegate.handleText(data, pos);
}
@Override
public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) {
delegate.handleStartTag(t, a, pos);
}
@Override
public void handleEndTag(HTML.Tag t, int pos) {
delegate.handleEndTag(t, pos);
}
@Override
public void handleSimpleTag(HTML.Tag t, MutableAttributeSet a, int pos) {
delegate.handleSimpleTag(t, a, pos);
}
@Override
public void handleComment(char[] data, int pos) {
delegate.handleComment(data, pos);
}
@Override
public void handleEndOfLineString(String eol) {
delegate.handleEndOfLineString(eol);
}
}
}
// 使用时:
JEditorPane editor = new JEditorPane();
editor.setEditorKit(new CustomHTMLEditorKit());
editor.setText("<html><body><p>Test<b>No close</p></html>");
预处理 HTML 以修复常见错误
在传给 JEditorPane 之前,使用简单规则清理 HTML:
public static String sanitizeHtml(String html) {
if (html == null) return "";
// 关闭常见的未闭合标签
html = html.replaceAll("<br>", "<br/>");
html = html.replaceAll("<img([^>]*)>", "<img$1/>");
// 确保 img 等自闭合标签正确
// 移除不合法的属性或标签(根据需要)
return html;
}
// 使用:
String cleanHtml = sanitizeHtml(badHtml);
editorPane.setText(cleanHtml);
使用更健壮的解析器(如 JSoup)预处理
如果项目允许添加依赖,推荐使用 JSoup 清理 HTML:
import org.jsoup.Jsoup;
import org.jsoup.safety.Safelist;
public static String cleanWithJsoup(String dirtyHtml) {
// 使用宽松的安全列表,保留大多数标签和属性
String cleaned = Jsoup.clean(dirtyHtml, Safelist.relaxed());
// 或者使用更精确的:Safelist.basic() / Safelist.simpleText()
return cleaned;
}
//
String safeHtml = cleanWithJsoup(badHtml);
editorPane.setText(safeHtml);
设置全局错误监听(高级)
通过重写 HTMLEditorKit.ParserCallback 的 handleError 方法,可以捕获所有解析错误,上面的 ErrorHandlingCallback 已经演示了这一点,你也可以直接在默认的 HTMLEditorKit 中使用:
HTMLEditorKit kit = new HTMLEditorKit();
HTMLDocument doc = (HTMLDocument) kit.createDefaultDocument();
doc.setParser(new ParserDelegator() {
@Override
public void parse(Reader r, HTMLEditorKit.ParserCallback cb, boolean ignoreCharSet) throws IOException {
super.parse(r, new HTMLEditorKit.ParserCallback() {
@Override
public void handleError(String errorMsg, int pos) {
System.err.println("Error: " + errorMsg + " at " + pos);
// 可以继续向原始回调传递错误,或忽略
if (cb != null) cb.handleError(errorMsg, pos);
}
// 其他方法委托给 cb
// 注意:所有方法都需要重写并委托
}, ignoreCharSet);
}
});
editorPane.setDocument(doc);
| 方法 | 适用场景 | 复杂度 |
|---|---|---|
| 简单 try-catch | 仅需基本容错 | 低 |
| 自定义 Kit + 错误回调 | 需要记录/跟踪具体解析错误 | 中 |
| 预处理 HTML(正则/Soup) | 可控的输入源,可提前清理 | 中 |
| 更换解析器(JSoup 预处理) | 项目允许依赖,需高质量清理 | 高(但效果好) |
推荐做法:
- HTML 来源不可控(如用户输入),先用 JSoup 清理,再传给
JEditorPane。 - 如果必须使用 Swing 原生解析器,则实现 错误回调 捕获
handleError,避免静默失败。