您似乎有两个明确的要求:
- 您需要将HTML表示为
org.w3c.dom.Document
。
- 您需要使
Document#getElementsByTagName(String tagname)
在不区分大小写的情况下操作。
如果您正在使用
org.w3c.dom.Document
处理HTML,则我假设您正在使用某种XHTML版本。因为XML API(如DOM)将期望格式良好的XML。HTML并不一定是格式良好的XML,但XHTML是格式良好的XML。即使您使用HTML工作,也必须对其进行一些预处理,以确保它是格式良好的XML,然后再尝试通过XML解析器运行它。可能更容易首先使用HTML解析器(例如
jsoup)解析HTML,然后通过遍历HTML解析器生成的树(在jsoup的情况下是
org.jsoup.nodes.Document
)构建您的
org.w3c.dom.Document
。
有一个org.w3c.dom.html.HTMLDocument
接口,它扩展了org.w3c.dom.Document
。我找到的唯一实现是在Xerces-j(2.11.0)中以org.apache.html.dom.HTMLDocumentImpl的形式出现。乍一看这似乎很有前途,但仔细检查后,我们发现存在一些问题。
1. 没有明确的“清晰”的方法来获取实现org.w3c.dom.html.HTMLDocument
接口的对象实例。
使用Xerces,我们通常会使用DocumentBuilder
以以下方式获取Document
对象:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.newDocument();
或者使用 DOMImplementation
类型:
DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance()
DOMImplementationLS impl = (DOMImplementationLS)registry.getDOMImplementation("LS")
LSParser lsParser = impl.createLSParser(DOMImplementationLS.MODE_SYNCHRONOUS, null)
Document document = lsParser.parseURI("myFile.xml")
在这两种情况下,我们纯粹使用
org.w3c.dom.*
接口来获取
Document
对象。
我找到的与
HTMLDocument
最接近的等效物是这样的:
HTMLDOMImplementation htmlDocImpl = HTMLDOMImplementationImpl.getHTMLDOMImplementation()
HTMLDocument htmlDoc = htmlDocImpl.createHTMLDocument("My Title")
这需要我们直接实例化内部实现类,使我们的实现依赖于Xerces。
(注意:我还看到Xerces也有一个内部的HTMLBuilder
(它实现了已弃用的DocumentHandler
),可以使用SAX解析器生成HTMLDocument
,但我没有深入研究。)
2. org.w3c.dom.html.HTMLDocument
不能生成正确的XHTML。
虽然你可以使用getElementsByTagName(String tagname)
以不区分大小写的方式搜索HTMLDocument
树,但所有元素名称都保存在内部的大写字母中。但是XHTML元素和属性名称应该是全部小写。(这可以通过遍历整个文档树并使用Document
的renameNode()
方法将所有元素的名称更改为小写来解决。)
此外,XHTML文档应该有适当的
DOCTYPE声明和
XHTML命名空间的xmlns声明。在
HTMLDocument
中似乎没有简单的方法来设置这些(除非你对内部Xerces实现进行一些调整)。
org.w3c.dom.html.HTMLDocument
文档很少,接口的Xerces实现似乎不完整。我没有搜遍整个互联网,但我找到的唯一关于
HTMLDocument
的文档是先前链接的JavaDocs,以及Xerces内部实现的源代码注释。在这些注释中,我还发现了几个接口的不同部分没有实现。(旁注:我真的觉得
org.w3c.dom.html.HTMLDocument
接口本身并没有被任何人使用,并且可能本身也是不完整的。)
由于这些原因,我认为最好避免使用
org.w3c.dom.html.HTMLDocument
,而是尽可能利用
org.w3c.dom.Document
。我们能做什么呢?
一种方法是扩展
org.apache.xerces.dom.DocumentImpl
(它扩展了
org.apache.xerces.dom.CoreDocumentImpl
,实现了
org.w3c.dom.Document
)。这种方法不需要太多的代码,但它仍然使我们依赖于Xerces的实现,因为我们正在扩展
DocumentImpl
。在我们的
MyHTMLDocumentImpl
中,我们只是在元素创建和搜索时将所有标签名称转换为小写。这将允许以不区分大小写的方式使用
Document#getElementsByTagName(String tagname)
。
import org.apache.xerces.dom.DocumentImpl;
import org.apache.xerces.dom.DocumentTypeImpl;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentType;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class MyHTMLDocumentImpl extends DocumentImpl {
private static final long serialVersionUID = 1658286253541962623L;
public static Document makeBasicHtmlDoc(String title) {
Document htmlDoc = new MyHTMLDocumentImpl();
DocumentType docType = new DocumentTypeImpl(null, "html",
"-//W3C//DTD XHTML 1.0 Strict//EN",
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd");
htmlDoc.appendChild(docType);
Element htmlElement = htmlDoc.createElementNS("http://www.w3.org/1999/xhtml", "html");
htmlDoc.appendChild(htmlElement);
Element headElement = htmlDoc.createElement("head");
htmlElement.appendChild(headElement);
Element titleElement = htmlDoc.createElement("title");
if(title != null)
titleElement.setTextContent(title);
headElement.appendChild(titleElement);
Element bodyElement = htmlDoc.createElement("body");
htmlElement.appendChild(bodyElement);
return htmlDoc;
}
public static Document createFrom(Document doc) {
Document htmlDoc = new MyHTMLDocumentImpl();
DocumentType originDocType = doc.getDoctype();
if(originDocType != null) {
DocumentType docType = new DocumentTypeImpl(null, originDocType.getName(),
originDocType.getPublicId(),
originDocType.getSystemId());
htmlDoc.appendChild(docType);
}
Node docElement = doc.getDocumentElement();
if(docElement != null) {
Node copiedDocElement = docElement.cloneNode(true);
htmlDoc.adoptNode(copiedDocElement);
htmlDoc.appendChild(copiedDocElement);
}
return htmlDoc;
}
private MyHTMLDocumentImpl() {
super();
}
@Override
public Element createElement(String tagName) throws DOMException {
return super.createElement(tagName.toLowerCase());
}
@Override
public Element createElementNS(String namespaceURI, String qualifiedName) throws DOMException {
return super.createElementNS(namespaceURI, qualifiedName.toLowerCase());
}
@Override
public NodeList getElementsByTagName(String tagname) {
return super.getElementsByTagName(tagname.toLowerCase());
}
@Override
public NodeList getElementsByTagNameNS(String namespaceURI, String localName) {
return super.getElementsByTagNameNS(namespaceURI, localName.toLowerCase());
}
@Override
public Node renameNode(Node n, String namespaceURI, String qualifiedName) throws DOMException {
return super.renameNode(n, namespaceURI, qualifiedName.toLowerCase());
}
}
测试人员:
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.w3c.dom.DOMConfiguration;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.bootstrap.DOMImplementationRegistry;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSOutput;
import org.w3c.dom.ls.LSSerializer;
public class HTMLDocumentTest {
private final static int P_ELEMENT_NUM = 3;
public static void main(String[] args)
throws ClassNotFoundException, InstantiationException, IllegalAccessException, ClassCastException, IOException {
Document htmlDoc = MyHTMLDocumentImpl.makeBasicHtmlDoc("My Title");
Element bodyElement = (Element) htmlDoc.getElementsByTagName("body").item(0);
for(int i = 0; i < P_ELEMENT_NUM; ++i) {
Element pElement = htmlDoc.createElement("p");
String id = Integer.toString(i+1);
pElement.setAttribute("id", "anId"+id);
pElement.setTextContent("Here is some text"+id+".");
bodyElement.appendChild(pElement);
}
NodeList titleNodeList = htmlDoc.getElementsByTagName("tItLe");
for(int i = 0; i < titleNodeList.getLength(); ++i)
System.out.println(titleNodeList.item(i).getTextContent());
System.out.println();
{
NodeList pNodeList = htmlDoc.getElementsByTagName("p");
for(int i = 0; i < pNodeList.getLength(); ++i) {
System.out.println(pNodeList.item(i).getTextContent());
}
}
System.out.println();
{
NodeList pNodeList = htmlDoc.getElementsByTagName("P");
for(int i = 0; i < pNodeList.getLength(); ++i) {
System.out.println(pNodeList.item(i).getTextContent());
}
}
System.out.println();
DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
DOMImplementationLS domImplLS = (DOMImplementationLS) registry.getDOMImplementation("LS");
LSSerializer lsSerializer = domImplLS.createLSSerializer();
DOMConfiguration domConfig = lsSerializer.getDomConfig();
domConfig.setParameter("format-pretty-print", true);
LSOutput lsOutput = domImplLS.createLSOutput();
lsOutput.setEncoding("UTF-8");
try (OutputStream os = new FileOutputStream(new File("myFile.html"))) {
lsOutput.setByteStream(os);
lsSerializer.write(htmlDoc, lsOutput);
}
System.out.println(lsSerializer.writeToString(htmlDoc));
}
}
输出:
My Title
Here is some text1.
Here is some text2.
Here is some text3.
Here is some text1.
Here is some text2.
Here is some text3.
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>My Title</title>
</head>
<body>
<p id="anId1">Here is some text1.</p>
<p id="anId2">Here is some text2.</p>
<p id="anId3">Here is some text3.</p>
</body>
</html>
另一种类似于上述方法的方法是创建一个
Document
包装器,它包装了一个
Document
对象并实现了
Document
接口。这比“扩展
DocumentImpl
”方法需要更多的代码,但这种方法更加“清洁”,因为我们不必关心特定的
Document
实现。这种方法的额外代码并不难写;只是需要提供所有那些包装器实现
Document
方法的代码有点繁琐。我还没有完全解决这个问题,可能会有一些问题,但如果可以工作,这就是一般的想法:
public class MyHTMLDocumentWrapper implements Document {
private Document doc;
public MyHTMLDocumentWrapper(Document doc) {
this.doc = doc;
}
}
无论是
org.w3c.dom.html.HTMLDocument
,我上面提到的方法之一,还是其他什么东西,也许这些建议可以帮助您了解如何继续进行。
编辑:
在尝试解析以下XHTML文件时,我的解析测试中,Xerces会在实体管理类中尝试打开http连接而挂起。我不知道为什么?特别是因为我在本地测试了一个没有实体的html文件。(可能与DOCTYPE或命名空间有关吗?)这是该文档:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>My Title</title>
</head>
<body>
<p id="anId1">Here is some text1.</p>
<p id="anId2">Here is some text2.</p>
<p id="anId3">Here is some text3.</p>
</body>
</html>