使用Java进行编程式HTML文档生成

12

有没有人知道如何在Java中以编程方式生成HTMLDocument对象,而不是借助于外部生成字符串,然后使用HTMLEditorKit#read进行解析?我提出这个问题有两个原因:

首先,我的HTML生成例程需要非常快速,我认为将字符串解析为内部模型比直接构建此模型更加昂贵。

其次,面向对象的方法可能会导致更清晰的代码。

我还应该提到,基于许可证原因,我不能使用除JVM附带库之外的任何库。


你为什么需要解析你正在生成的HTML?你是否需要能够插入可能会使其无效的内联HTML? - Oliver N.
感谢您的提问:mmyers: HTMLOliver: 抱歉,我没有表达清楚。如果我正确理解了您的问题,我正在使用HTMLEditorKit#read生成一个HTMLDocument,以便由JTextPane呈现。 - Tom Klapiscak
再说一遍,这并不能解释为什么你需要生成然后解析。 - Tetsujin no Oni
这不是我在进行解析。然而,我认为Swing必须在底层解析HTML以便在JTextArea中呈现HTML(否则每次重新绘制面板时都会不必要地重新解析对象)。我想找到一种跳过此步骤的方法,直接生成对象,而不是生成一个字符串(Swing可能会将其解析为对象)。 - Tom Klapiscak
既然你有一个定义好的目标对象(HTMLDocument)需要生成,那么如果API是为此而设计的话,代码将会更加简洁。如果API只考虑了字符串作为源,那么迫使你的代码使用其解析方法可能会更快,但几乎肯定不会更加简洁。 - Yishai
9个回答

9

一种面向对象的方法是使用一个名为 ECS 的库。

这是一个非常简单的库,而且已经有很长时间没有更新了。但是,HTML 4.01规范也没有变化 ;) 我曾经使用过ECS,并认为它比仅使用字符串或StringBuffers/StringBuilders生成大型HTML片段要好得多。

小例子:

Option optionElement = new Option();
optionElement.setTagText("bar");
optionElement.setValue("foo");
optionElement.setSelected(false);   

optionElement.toString()现在将产生以下结果:

<option value='foo'>bar</option>

该库支持HTML 4.0和XHTML。最初让我非常困扰的是,与XHTML版本相关的类名称以小写字母开头:optioninputatr等等,这违反了大多数Java基本约定。但如果你想使用XHTML,你可以适应它; 我惊讶地发现我很快就习惯了。

1
汤姆不能直接使用该库(虽然我不知道为什么有人会对Apache许可证有问题),但他可以查看API以获取灵感。 - Kathy Van Stone
嗯,我很确定“除了随JVM一起提供的库之外,我不能使用任何其他库”不是问题的原始版本! :) 有了这个限制,JeeBee的(实用类如TableBuilder)和Adam Paynter的(XMLStreamWriter)解决方案似乎是合理的。 - Jonik

7

我建议您学习一下JSP的工作原理,即它们编译成一个基本上是一系列StringBuffer append的servlet。标签也会编译成Java代码片段。这很混乱,但非常非常快,除非您深入Tomcat的工作目录,否则您永远看不到这些代码。也许您想要的是像JSP那样从HTML为中心的视角编写您的HTML生成,添加循环等标签,并在项目内部使用类似的代码生成引擎和编译器。

或者,您可以自己处理StringBuilder,在一个实用程序类中拥有“openTag”,“closeTag”,“openTagWithAttributes”,“startTable”等方法……它可以使用构建器模式,您的代码将如下所示:

public static void main(String[] args) {
    TableBuilder t = new TableBuilder();
    t.start().border(3).cellpadding(4).cellspacing(0).width("70%")
      .startHead().style("font-weight: bold;")
        .newRow().style("border: 2px 0px solid grey;")
          .newHeaderCell().content("Header 1")
          .newHeaderCell().colspan(2).content("Header 2")
      .end()
      .startBody()
        .newRow()
          .newCell().content("One/One")
          .newCell().rowspan(2).content("One/Two")
          .newCell().content("One/Three")
        .newRow()
          .newCell().content("Two/One")
          .newCell().content("Two/Three")
      .end()
    .end();
    System.out.println(t.toHTML());
}

我在这里使用了TableBuilder,因为我有代码,因为我们在项目中需要将HTML表嵌入HTML电子邮件中。编写起来并不难,但是您需要跟踪开放标记和当前状态。 - JeeBee

4

在处理XHTML时,我使用Java 6的XMLStreamWriter接口取得了很大的成功。

OutputStream destination = ...;
XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
XMLStreamWriter xml = outputFactory.createXMLStreamWriter(destination);

xml.writeStartDocument();
xml.writeStartElement("html");
xml.writeDefaultNamespace("http://www.w3.org/1999/xhtml");

xml.writeStartElement("head");
xml.writeStartElement("title");
xml.writeCharacters("The title of the page");
xml.writeEndElement();
xml.writeEndElement();

xml.writeEndElement();
xml.writeEndDocument();

3

我认为通过类似于 StringBuilder(或直接到流)手动生成 HTML 将是您最佳的选择,特别是如果您不能使用任何外部库。

无法使用任何外部库将会在开发速度上受到更大的影响,而不是性能。


1
+1 是最快的,而且肮脏的方面可以隐藏在实用程序构建类中,正如我在这个问题上的回复所述。 - JeeBee

2

javax.swing.text.html中包含了HTMLWriter和HTMLDocument等类。我没有使用过它们。我在.Net中使用了HtmlWriter,它可以完全满足您的需求,但是Java版本可能不会完全相同。

这是文档:http://java.sun.com/j2se/1.5.0/docs/api/javax/swing/text/html/HTMLWriter.html

此外,我无法想象StringBuilder比对象层次结构构建更慢。在我看来,任何面向对象的方法都必须构建对象图,然后生成字符串。不使用原始字符串的主要原因是会产生编码错误以及其他导致文档格式不正确的错误。

选项2:您可以使用您喜欢的XML api并生成XHTML。


1
你可能需要构建一些具有render()方法的Element对象,然后将它们组装成树形结构;使用访问算法,您可以继续设置值,然后渲染整个结构。
PS:您是否考虑过像freemarker这样的模板引擎?

1

看起来您可以通过直接构建 HTMLDocument.BlockElementHTMLDocument.BlockElement 对象来完成您正在尝试的操作。这些构造函数的签名表明直接使用是可能的,至少如此。

我建议您检查OpenJDK中Swing源代码的处理方式,并从中推导出您的逻辑。

我还建议,如果这确实成为应用程序中的性能热点,那么这种优化可能是过早的,也许应该将其作为一个简单方法(即生成HTML文本)的速度优化替代方案。


0

你可以使用任何像JDom、Xom或XStream这样的良好的XML库。HTML只是XML的一个特例。

或者,你可以使用现有的服务器端Java模板引擎之一,如JSP或Velocity。


从技术上讲,HTML不是XML的特例。XHTML是。 - Mr. Shiny and New 安宇
XHTML(强调X)是XML。HTML是SGML。它们相似但实际上并不是同一种东西。大多数有效的XHTML也是有效的HTML,但并非全部。 - Licky Lindsay
2
由于它被用在JEditorPane中,所以它只需要是一种该面板可以读取的HTML形式。 - Kathy Van Stone
是的,这就是问题的关键,Kathy :) - Tom Klapiscak

0

基本上,您可以使用其中一种插入方法(insertBeforeEnd(),insertAfterEnd(),insertBeforeStart(),insertAfterStart())将HTML插入到HTMLDocument中。您提供要插入的html和要插入html的文档树中的位置。

例如:

doc.insertBeforeEnd(element,html);

HTMLDocument类还提供了遍历文档树的方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接