JEditorPaneHTMLEditorKitParserQuotient商处理

wen java案例 2

本文目录导读:

JEditorPaneHTMLEditorKitParserQuotient商处理

  1. 问题根因
  2. 解决方案矩阵
  3. 调试技巧
  4. 总结建议

在处理 JEditorPaneHTMLEditorKit 对 HTML 中的特殊字符(如 "" 导致的解析商/解析差异)时,核心问题通常在于 HTML 实体编码与 Swing HTML 渲染器解析机制的耦合

以下是针对“商处理”(即特殊字符 quot 与尖括号、引号之间的解析优先级冲突)的深度分析与解决方案:

问题根因

在 Java Swing 的 HTMLEditorKit 底层(基于 javax.swing.text.html.parser.ParserDelegatorDocumentParser)中,解析器遵循严格的 SGML/HTML 4.0 规则:

  1. 实体优先" 在属性值内会被解析为双引号 。
  2. 属性值结束符:双引号 是属性值的结束标记,如果用户输入的 quot 被错误地转换为 ,而该 又恰好出现在属性值中,会导致解析器提前结束属性值,从而破坏 DOM 树结构(即“商处理”的歧义)。

典型场景

这段代码在标准浏览器中正确渲染,但在 JEditorPane 中,"重复转义前置转义上下文不匹配,可能变成:

此时解析器会将第一个 视为 title 属性值的结束,导致 Hello 变成意外的标签/文本。

解决方案矩阵

严格实体编码(推荐)

在将字符串设置到 JEditorPane 之前,手动进行 双重保护规范化编码

public static String sanitizeHTMLForSwing(String html) {
    // 1. 先保护所有现有的 & 符号(防止二次转义)
    html = html.replace("&", "&");
    // 2. 对可能破坏属性的引号进行显式实体化
    //    注意:不要直接对 " 再编码,而是处理原始字符
    html = html.replace("\"", """);
    // 3. 恢复允许的合法实体(避免 " 变成 ")
    //    这一步骤需要根据具体需求调整
    html = html.replace(""", """);
    return html;
}
// 使用示例
String rawContent = "<span title='User said \"Hello\"'>Content</span>";
String safeContent = sanitizeHTMLForSwing(rawContent);
editorPane.setText("<html><body>" + safeContent + "</body></html>");

使用 CDATA 或自定义编码方案(极限场景)包含大量复杂嵌套引号,可以临时替换为不可见字符,设置后再恢复:

public void setComplexHTML(JEditorPane editor, String html) {
    // 使用 Unicode 零宽空格作为占位符
    String placeholder = "\u200B"; 
    // 将 &quot; 暂时替换为占位符
    String processed = html.replace("&quot;", placeholder);
    // 设置到编辑器
    editor.setText(processed);
    // 延迟恢复(需要等到解析完成后)
    SwingUtilities.invokeLater(() -> {
        String current = editor.getText();
        String restored = current.replace(placeholder, "\"");
        if (!restored.equals(current)) {
            editor.setText(restored);
        }
    });
}

改变解析器行为(高级)

如果你有足够的控制权,可以替换默认的 ParserDelegator,自定义实体处理逻辑:

editorPane.setEditorKit(new HTMLEditorKit() {
    @Override
    public Document createDefaultDocument() {
        Document doc = super.createDefaultDocument();
        // 通过 StyleSheet 或自定义解析器过滤实体
        return doc;
    }
});
// 或者直接操作底层解析器参数
HTMLEditorKit kit = (HTMLEditorKit) editorPane.getEditorKit();
// 注:Swing 的 HTMLEditorKit 没有直接公开实体映射修改接口
// 通常需要通过继承 com.sun.java.swing.text.html.parser.Parser 实现

使用 JTextPane + 手动片段构建(绕过 HTML 解析)

如果只是显示文本中包含 &quot;,且不想受 HTML 解析影响,可以直接操作文档对象:

// 彻底绕过 HTML 解析器
JTextPane textPane = new JTextPane();
StyledDocument doc = textPane.getStyledDocument();
try {
    // 手动插入文本,将 &quot; 作为普通文本处理
    doc.insertString(0, "He said &quot;Hello&quot;", null);
    // 如果需要样式,使用 AttributeSet
    SimpleAttributeSet attrs = new SimpleAttributeSet();
    StyleConstants.setBold(attrs, true);
    doc.insertString(doc.getLength(), "Bold text with &quot;", attrs);
} catch (BadLocationException e) {
    e.printStackTrace();
}

调试技巧

当出现“商处理”异常时,打印解析后的 DOM 结构:

// 在设置内容后,获取底层 HTML 文档结构
HTMLDocument doc = (HTMLDocument) editorPane.getDocument();
Element root = doc.getDefaultRootElement();
// 递归遍历元素树,检查属性值是否被截断
printElementHierarchy(root, 0);

总结建议

场景 推荐方案
简单文本含少量 &quot; 方案1:严格实体编码
动态构建复杂 HTML 属性 方案1 + 使用 XML 库预构建并序列化
实时输入/编辑中的转义 方案4:JTextPane + 手动文档操作
已有大量遗留代码且无法大改 方案2:占位符延迟替换

最关键的一点:Swing 的 HTML 渲染器是一个受限的子集实现(不支持 CSS3、部分 HTML4 功能),建议尽量避免在属性值中使用复杂引号嵌套,如果必须处理,优先使用 方案1 的显式实体编码,并在设置前后进行内容校验。

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