在做解析之前,我们先构建一个xml,以后相关的解析都针对这个xml进行操作。
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book id="1">
<name>冰与火之歌</name>
<author>乔治马丁</author>
<year>2014</year>
<price>89</price>
</book>
<book id="2">
<name>安徒生童话</name>
<year>2004</year>
<price>77</price>
<language>English</language>
</book>
</bookstore>
原理
DOM的全称是Document Object Model,也即文档对象模型。
xml解析器一次性把整个xml文档加载进内存,然后在内存中构建一颗Document的对象树,通过Document对象,得到树上的节点对象,通过节点对象访问(操作)到xml文档的内容。
Document对象代表了一个完整的xml文档,通过Document对象,可以得到其下面的其他节点对象,通过各个节点对象来访问xml文档的内容。
其中主要包括:标签节点,属性节点,文本节点和注释节点;并且各类节点也被封装成对应的对象,通过操作不同的对象来访问xml的内容。
优缺点
优点:
1、形成了树结构,有助于更好的理解、掌握,且代码容易编写。
2、解析过程中,树结构保存在内存中,方便修改。
缺点:
1、由于文件是一次性读取,所以对内存的耗费比较大。
2、如果XML文件比较大,容易影响解析性能且可能会造成内存溢出。
解析过程
-
建立一个解析器工厂
//创建一个DocumentBuilderFactory的对象 //DocumentBuilderFactory是一个抽象工厂类,它不能直接实例化,但该类提供了一个newInstance方法 ,这个方法会根据本地平台默认安装的解析器,自动创建一个工厂的对象并返回 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
使用DocumentBuilderFacotry的目的是为了创建与具体解析器无关的程序,当 DocumentBuilderFactory类的静态方法newInstance()被调用时,它根据一个系统变量来决定具体使用哪一个解析器。又因为 所有的解析器都服从于JAXP所定义的接口,所以无论具体使用哪一个解析器,代码都是一样的。所以当在不同的解析器之间进行切换时,只需要更改系统变量的 值,而不用更改任何代码。这就是工厂所带来的好处。
-
获得一个DocumentBuilder对象
//调用工厂对象的 newDocumentBuilder方法得到 DOM 解析器对象 DocumentBuilder db = dbf.newDocumentBuilder();
newDocumentBuilder()是一个抽象方法,通过它可以获得一个DocumentBuilder解析器对象,我们就可以利用这个解析器来对XML文档进行解析。
-
获取Document对象
Document document = db.parse("books.xml"); Document document = db.parse(new File("books.xml"));
DocumentBuilder的parse()方法接受一个XML文档名作为输入参数,返回一个Document对象,这个Document对象就 代表了一个XML文档的树模型。以后所有的对XML文档的操作,都与解析器无关,直接在这个Document对象上进行操作就可以。
这个方法进行了重载,可以接受不同的参数,返回Document对象。public Document parse(InputStream is) throws SAXException, IOException { if (is == null) { throw new IllegalArgumentException("InputStream cannot be null"); } InputSource in = new InputSource(is); return parse(in); } public Document parse(InputStream is, String systemId) throws SAXException, IOException { if (is == null) { throw new IllegalArgumentException("InputStream cannot be null"); } InputSource in = new InputSource(is); in.setSystemId(systemId); return parse(in); } public Document parse(String uri) throws SAXException, IOException { if (uri == null) { throw new IllegalArgumentException("URI cannot be null"); } InputSource in = new InputSource(uri); return parse(in); } public Document parse(File f) throws SAXException, IOException { if (f == null) { throw new IllegalArgumentException("File cannot be null"); } InputSource in = new InputSource(f.toURI().toASCIIString()); return parse(in); } public abstract Document parse(InputSource is) throws SAXException, IOException;
-
Dom操作
因为xml以读取居多,很少涉及到dom更改和新增,所以这里不进行这部分操作了,有兴趣自己去百度就行。public static void main(String[] args) throws IOException, SAXException, ParserConfigurationException { //创建一个DocumentBuilderFactory的对象 //DocumentBuilderFactory是一个抽象工厂类,它不能直接实例化,但该类提供了一个newInstance方法 ,这个方法会根据本地平台默认安装的解析器,自动创建一个工厂的对象并返回 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); //调用工厂对象的 newDocumentBuilder方法得到 DOM 解析器对象 DocumentBuilder db = dbf.newDocumentBuilder(); //通过DocumentBuilder对象的parser方法加载books.xml文件到当前项目下 Document document = db.parse("C:\\Users\\Administrator\\Desktop\\books.xml"); //去掉XML文档中作为格式化内容的空白而映射在DOM树中 的不必要的Text Node对象 document.normalize(); //获取根节点的元素对象 Element root = document.getDocumentElement(); listNodes(root); //获取所有book节点的集合 NodeList nodeList = document.getElementsByTagName("book"); for (int i = 0; i < nodeList.getLength(); i++) { //通过 item(i)方法 获取一个节点,nodelist的索引值从0开始 Node node = nodeList.item(i); listNodes(node); } } /** * 遍历指定节点 * @param node */ public static void listNodes(Node node) { // 节点是什么类型的节点 if (node.getNodeType() == Node.ELEMENT_NODE) {// 判断是否是元素节点 Element element = (Element) node; System.out.println("name:" + element.getNodeName() + " value:" + element.getNodeValue() + " type:" + element.getNodeType() + " text:"+element.getTextContent()); //判断此元素节点是否有属性 if(element.hasAttributes()){ //获取属性节点的集合 NamedNodeMap namenm = element.getAttributes();//Node //遍历属性节点的集合 for(int k=0;k<namenm.getLength();k++){ //获取具体的某个属性节点 Attr attr = (Attr) namenm.item(k); System.out.println("attrName:"+attr.getNodeName()+" attrValue:" +attr.getNodeValue()+" attrType:"+attr.getNodeType()); } } //获取元素节点的所有孩子节点 NodeList listnode = element.getChildNodes(); //遍历 for (int j = 0; j < listnode.getLength(); j++) { //得到某个具体的节点对象 Node nd = listnode.item(j); //重新调用遍历节点的操作的方法 listNodes(nd); } }