JEditorPaneHTMLEditorKitParserMethodNegativeArraySizeException负数组大小

wen java案例 1

本文目录导读:

JEditorPaneHTMLEditorKitParserMethodNegativeArraySizeException负数组大小

  1. 问题原因
  2. 解决方案
  3. 推荐的最佳实践

这是一个典型的 Java Swing 中 JEditorPane 配合 HTMLEditorKit 解析 HTML 时出现的 NegativeArraySizeException(负数组大小异常) 问题。

问题原因

这个异常通常是由于 HTML 解析器在解析包含特定格式错误的 HTML 内容时,计算出错误的数组长度 导致的。

  1. HTML 内容中包含过大的数字或特殊字符(如表格 colspanrowspanwidth 等属性值异常)
  2. 解析器在计算数组大小时得到负数(如 new int[-1]
  3. HTML 标签嵌套不正确属性值格式异常

解决方案

验证并清理 HTML 内容

public class SafeHtmlEditorPane extends JEditorPane {
    public void setSafeHtml(String html) {
        try {
            // 清理可能导致问题的属性值
            String cleanedHtml = cleanProblematicAttributes(html);
            this.setContentType("text/html");
            this.setText(cleanedHtml);
        } catch (NegativeArraySizeException e) {
            System.err.println("HTML解析错误,使用备用显示方式");
            this.setText("内容格式错误,无法正确显示");
        }
    }
    private String cleanProblematicAttributes(String html) {
        // 替换异常的colspan/rowspan值
        html = html.replaceAll("colspan=\"(\\d+)\"", match -> {
            String numStr = match.replaceAll("[^0-9]", "");
            int num = Integer.parseInt(numStr);
            if (num < 1 || num > 100) {
                return "colspan=\"1\""; // 替换为安全值
            }
            return match;
        });
        // 清除超大width/height值
        html = html.replaceAll("width=\"(\\d+)\"", match -> {
            String numStr = match.replaceAll("[^0-9]", "");
            int num = Integer.parseInt(numStr);
            if (num < 0 || num > 10000) {
                return "width=\"100\""; // 默认安全值
            }
            return match;
        });
        return html;
    }
}

使用自定义 HTMLEditorKit

public class SafeHTMLEditorKit extends HTMLEditorKit {
    @Override
    public Document createDefaultDocument() {
        HTMLDocument doc = (HTMLDocument) super.createDefaultDocument();
        doc.setAsynchronousLoadPriority(-1); // 同步加载
        return doc;
    }
}
// 使用方式
JEditorPane editorPane = new JEditorPane();
editorPane.setEditorKit(new SafeHTMLEditorKit());

异常捕获处理

public void loadHtmlContent(JEditorPane editorPane, String htmlContent) {
    try {
        // 设置HTML内容
        editorPane.setContentType("text/html");
        // 分步设置以减少解析压力
        SwingUtilities.invokeLater(() -> {
            try {
                editorPane.setText(htmlContent);
            } catch (NegativeArraySizeException e) {
                handleParsingError(editorPane, e);
            }
        });
    } catch (Exception e) {
        handleParsingError(editorPane, e);
    }
}
private void handleParsingError(JEditorPane pane, Exception e) {
    System.err.println("HTML解析异常:");
    e.printStackTrace();
    // 显示纯文本版本
    pane.setContentType("text/plain");
    pane.setText("【HTML解析错误】内容无法正常显示");
    // 或者显示错误信息
    pane.setText("Error: " + e.getMessage());
}

使用第三方库进行预处理

import org.jsoup.Jsoup;
import org.jsoup.safety.Whitelist;
public String sanitizeHtml(String dirtyHtml) {
    // 使用 Jsoup 清理和标准化 HTML
    org.jsoup.nodes.Document doc = Jsoup.parse(dirtyHtml);
    // 修复常见问题
    doc.outputSettings().prettyPrint(false);
    // 获取清理后的HTML
    String cleanHtml = doc.body().html();
    // 移除可能导致问题的属性
    cleanHtml = cleanHtml.replaceAll("colspan=\"-?\\d+\"", "colspan=\"1\"");
    cleanHtml = cleanHtml.replaceAll("rowspan=\"-?\\d+\"", "rowspan=\"1\"");
    return cleanHtml;
}

完整的解决示例

import javax.swing.*;
import javax.swing.text.html.*;
import java.awt.*;
public class SafeHtmlViewer extends JFrame {
    private JEditorPane editorPane;
    public SafeHtmlViewer() {
        editorPane = new JEditorPane();
        editorPane.setEditable(false);
        // 使用安全的 EditorKit
        editorPane.setEditorKit(createSafeHtmlKit());
        JScrollPane scrollPane = new JScrollPane(editorPane);
        add(scrollPane, BorderLayout.CENTER);
        setSize(600, 400);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
    private HTMLEditorKit createSafeHtmlKit() {
        return new HTMLEditorKit() {
            @Override
            public Document createDefaultDocument() {
                HTMLDocument doc = (HTMLDocument) super.createDefaultDocument();
                // 设置解析器属性
                doc.setParser(new HTMLDocument.HTMLReader.Parser() {
                    @Override
                    public void parse(String in) {
                        try {
                            super.parse(in);
                        } catch (NegativeArraySizeException e) {
                            System.err.println("解析器捕获负数组大小异常,尝试继续处理");
                        }
                    }
                });
                return doc;
            }
        };
    }
    public void displayHtml(String html) {
        try {
            // 预处理 HTML
            String safeHtml = preprocessHtml(html);
            editorPane.setText(safeHtml);
        } catch (Exception e) {
            // 最后保底方案
            editorPane.setText("内容显示异常");
            System.err.println("HTML显示失败: " + e.getMessage());
        }
    }
    private String preprocessHtml(String html) {
        if (html == null || html.isEmpty()) {
            return "<html><body></body></html>";
        }
        // 确保有基本结构
        if (!html.toLowerCase().startsWith("<html")) {
            html = "<html><body>" + html + "</body></html>";
        }
        return html;
    }
}

推荐的最佳实践

  1. 使用 Jsoup 库 预处理 HTML 内容
  2. 捕获异常并优雅降级,而不是让程序崩溃
  3. 限制输入内容的大小,避免超大 HTML 文件
  4. 在 EDT 线程中操作 UI 组件
  5. 测试各种异常 HTML 格式 提前发现问题

这个问题的根本原因是 JDK 内置的 HTML 解析器对某些格式错误的处理不够健壮,所以最好的策略是 在传给 JEditorPane 之前就清理和验证 HTML 内容

抱歉,评论功能暂时关闭!