本文目录导读:

针对 JEditorPane、HTMLEditorKit 和 HTML 解析输出的处理,这里整理了一份完整指南,涵盖常见场景和解决方案。
基础架构理解
JEditorPane
└── EditorKit (HTMLEditorKit)
└── Parser (默认是 javax.swing.text.html.parser.ParserDelegator)
└── Callback (HTMLEditorKit.ParserCallback)
└── 输出:各种 handleXxx 方法
核心处理方式
1 使用 ParserCallback 解析HTML
HTMLEditorKit.Parser parser = new HTMLEditorKit().getParser();
String html = "<html><body><h1>Title</h1><p>Content</p></body></html>";
Reader reader = new StringReader(html);
parser.parse(reader, new HTMLEditorKit.ParserCallback() {
@Override
public void handleText(char[] data, int pos) {
System.out.println("Text: " + new String(data));
}
@Override
public void handleStartTag(HTML.Tag tag, MutableAttributeSet attributes, int pos) {
System.out.println("Start Tag: " + tag + ", attrs: " + attributes);
}
@Override
public void handleEndTag(HTML.Tag tag, int pos) {
System.out.println("End Tag: " + tag);
}
@Override
public void handleSimpleTag(HTML.Tag tag, MutableAttributeSet attributes, int pos) {
System.out.println("Simple Tag: " + tag + ", attrs: " + attributes);
}
@Override
public void handleError(String errorMsg, int pos) {
System.err.println("Error at " + pos + ": " + errorMsg);
}
}, true); // true = ignore charset
2 提取纯文本内容
public String extractPlainText(String html) {
StringBuilder sb = new StringBuilder();
try {
HTMLEditorKit.Parser parser = new HTMLEditorKit().getParser();
Reader reader = new StringReader(html);
parser.parse(reader, new HTMLEditorKit.ParserCallback() {
@Override
public void handleText(char[] data, int pos) {
sb.append(data);
}
@Override
public void handleEndTag(HTML.Tag tag, int pos) {
if (tag == HTML.Tag.BR || tag == HTML.Tag.P || tag == HTML.Tag.DIV) {
sb.append("\n");
}
}
}, true);
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString().trim();
}
保存和恢复 HTML 内容
1 从 JEditorPane 获取 HTML
public String getHTMLContent(JEditorPane editorPane) {
// 方法1: 使用 Document 输出
StringWriter writer = new StringWriter();
try {
editorPane.getEditorKit().write(writer, editorPane.getDocument(), 0,
editorPane.getDocument().getLength());
return writer.toString();
} catch (IOException | BadLocationException e) {
e.printStackTrace();
}
return null;
// 方法2: 直接读取HTML
// return editorPane.getText(); // 获取纯文本
}
2 设置 HTML 内容
public void setHTMLContent(JEditorPane editorPane, String html) {
editorPane.setContentType("text/html");
editorPane.setText(html);
// 或者通过 Document
// try {
// editorPane.getEditorKit().read(
// new StringReader(html),
// editorPane.getDocument(),
// 0
// );
// } catch (Exception e) {
// e.printStackTrace();
// }
}
自定义解析器输出处理器
1 带状态追踪的解析器
public class AdvancedHTMLParser extends HTMLEditorKit.ParserCallback {
private final StringBuilder output = new StringBuilder();
private final Stack<HTML.Tag> tagStack = new Stack<>();
private boolean inScript = false;
private boolean inStyle = false;
@Override
public void handleStartTag(HTML.Tag tag, MutableAttributeSet attrs, int pos) {
tagStack.push(tag);
if (tag == HTML.Tag.SCRIPT) inScript = true;
if (tag == HTML.Tag.STYLE) inStyle = true;
// 自定义输出格式
if (!inScript && !inStyle) {
output.append("<").append(tag);
// 处理属性
Enumeration<?> names = attrs.getAttributeNames();
while (names.hasMoreElements()) {
Object name = names.nextElement();
output.append(" ").append(name)
.append("=\"").append(attrs.getAttribute(name)).append("\"");
}
output.append(">\n");
}
}
@Override
public void handleEndTag(HTML.Tag tag, int pos) {
if (tag == HTML.Tag.SCRIPT) inScript = false;
if (tag == HTML.Tag.STYLE) inStyle = false;
if (!tagStack.isEmpty()) {
tagStack.pop();
}
if (!inScript && !inStyle) {
output.append("</").append(tag).append(">\n");
}
}
@Override
public void handleText(char[] data, int pos) {
if (!inScript && !inStyle) {
output.append(new String(data)).append("\n");
}
}
public String getProcessedOutput() {
return output.toString();
}
}
2 统计信息收集器
public class HTMLStatisticsCollector extends HTMLEditorKit.ParserCallback {
private final Map<HTML.Tag, Integer> tagCount = new HashMap<>();
private int totalTextLength = 0;
private int linkCount = 0;
private int imageCount = 0;
@Override
public void handleStartTag(HTML.Tag tag, MutableAttributeSet attrs, int pos) {
tagCount.merge(tag, 1, Integer::sum);
if (tag == HTML.Tag.A) linkCount++;
if (tag == HTML.Tag.IMG) imageCount++;
// 收集特定属性
if (tag == HTML.Tag.A) {
String href = (String) attrs.getAttribute(HTML.Attribute.HREF);
if (href != null) {
System.out.println("Link found: " + href);
}
}
}
@Override
public void handleText(char[] data, int pos) {
totalTextLength += new String(data).trim().length();
}
public void printStatistics() {
System.out.println("=== HTML Statistics ===");
System.out.println("Total tags: " + tagCount.values().stream().mapToInt(Integer::intValue).sum());
System.out.println("Total text length: " + totalTextLength);
System.out.println("Links: " + linkCount);
System.out.println("Images: " + imageCount);
System.out.println("\nTag breakdown:");
tagCount.entrySet().stream()
.sorted(Map.Entry.<HTML.Tag, Integer>comparingByValue().reversed())
.forEach(e -> System.out.println(e.getKey() + ": " + e.getValue()));
}
}
实际应用示例
1 完整的HTML编辑器
import javax.swing.*;
import javax.swing.text.html.*;
import java.awt.*;
public class HTMLEditorExample {
private JEditorPane editor;
public void createAndShowGUI() {
JFrame frame = new JFrame("HTML Editor");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(600, 400);
editor = new JEditorPane();
editor.setContentType("text/html");
editor.setEditable(true);
editor.setText("<html><body><h1>Hello World</h1></body></html>");
// 添加按钮
JButton parseBtn = new JButton("Parse and Analyze");
parseBtn.addActionListener(e -> parseAndAnalyze());
JButton extractTextBtn = new JButton("Extract Text");
extractTextBtn.addActionListener(e -> extractText());
JPanel buttonPanel = new JPanel();
buttonPanel.add(parseBtn);
buttonPanel.add(extractTextBtn);
frame.add(new JScrollPane(editor), BorderLayout.CENTER);
frame.add(buttonPanel, BorderLayout.SOUTH);
frame.setVisible(true);
}
private void parseAndAnalyze() {
String htmlContent = getHTMLContent();
if (htmlContent != null) {
HTMLStatisticsCollector collector = new HTMLStatisticsCollector();
try {
HTMLEditorKit.Parser parser = new HTMLEditorKit().getParser();
parser.parse(new StringReader(htmlContent), collector, true);
collector.printStatistics();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
private void extractText() {
String htmlContent = getHTMLContent();
if (htmlContent != null) {
String plainText = extractPlainText(htmlContent);
JOptionPane.showMessageDialog(editor, plainText, "Extracted Text",
JOptionPane.INFORMATION_MESSAGE);
}
}
private String getHTMLContent() {
StringWriter writer = new StringWriter();
try {
editor.getEditorKit().write(writer, editor.getDocument(), 0,
editor.getDocument().getLength());
return writer.toString();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new HTMLEditorExample().createAndShowGUI());
}
}
2 自定义渲染输出
public class CustomHTMLRenderer {
public static String renderToCustomFormat(String html) {
StringBuilder sb = new StringBuilder();
try {
HTMLEditorKit.Parser parser = new HTMLEditorKit().getParser();
parser.parse(new StringReader(html), new HTMLEditorKit.ParserCallback() {
int indent = 0;
@Override
public void handleStartTag(HTML.Tag tag, MutableAttributeSet attrs, int pos) {
sb.append(" ".repeat(indent))
.append("[OPEN: ").append(tag);
if (attrs.getAttributeCount() > 0) {
sb.append(" attributes: ");
Enumeration<?> names = attrs.getAttributeNames();
while (names.hasMoreElements()) {
Object name = names.nextElement();
sb.append(name).append("=").append(attrs.getAttribute(name));
if (names.hasMoreElements()) sb.append(", ");
}
}
sb.append("]\n");
indent++;
}
@Override
public void handleEndTag(HTML.Tag tag, int pos) {
indent--;
sb.append(" ".repeat(indent))
.append("[CLOSE: ").append(tag).append("]\n");
}
@Override
public void handleText(char[] data, int pos) {
String text = new String(data).trim();
if (!text.isEmpty()) {
sb.append(" ".repeat(indent))
.append("TEXT: \"").append(text).append("\"\n");
}
}
}, true);
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}
}
常见问题与解决方案
1 中文乱码处理
// 设置正确的字符编码
HTMLEditorKit kit = new HTMLEditorKit();
kit.setDefaultCharset("UTF-8");
editorPane.setEditorKit(kit);
// 或者在解析时指定
parser.parse(new InputStreamReader(inputStream, "UTF-8"), callback, false);
2 处理不完整/错误HTML
// 使用更宽容的解析器 HTMLDocument doc = (HTMLDocument) editorPane.getDocument(); doc.setAsynchronousLoadPriority(-1); // 同步加载 doc.setPreservesUnknownTags(true); // 保留未知标签
3 性能优化
// 批量处理文档更新
editorPane.getDocument().addDocumentListener(new DocumentListener() {
private final Timer timer = new Timer(300, e -> processChanges());
@Override
public void insertUpdate(DocumentEvent e) {
timer.restart(); // 延迟300ms处理
}
@Override
public void removeUpdate(DocumentEvent e) {
timer.restart();
}
@Override
public void changedUpdate(DocumentEvent e) {
timer.restart();
}
private void processChanges() {
// 这里执行实际的解析和输出处理
}
});
最佳实践
- 始终在EDT线程中处理UI相关操作
- 对大文档使用异步解析
- 关闭自动加载图片:
editorPane.setAutoscrolls(false) - 使用HTMLDocument而不是直接操作文本
- 考虑使用第三方库(如jsoup)处理复杂HTML
这套方案涵盖了从基础解析到高级处理的各种场景,可以根据具体需求选择合适的实现方式。