Java 读取 xml 文件
<?xml version="1.0" encoding="UTF-8"?> <bookstore> <book id="1"> <name>冰与火之歌</name> <author>乔治马丁</author> <year>2014</year> <price>89</price> </book> </bookstore>
这是一个简单的xml文件,根节点是bookstore,book是bookstroe的子节点,book节点有属性id ,也有子节点name author year price。子节点name author year price没有属性 ,只有值。
1、DOM: Document Object Model 文档对象模型
DOM解析会一次性的将xml读取到内存中,并形成一个DOM树,如果xml非常大会浪费时间,对内存性能要求高。
优点:形成树结构,直观好理解,代码更易编写,解析过程中树结构保留在内存中,方便修改。
缺点:当xml文件较大时,对内存耗费比较大,容易影响解析性能并造成内存溢出。
package com.zhao.dom; import java.io.IOException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; public class DOMTest { /** * 应用DOM方式解析XML DOM: Document Object Model 文档对象模型 * DOM解析会一次性的将xml读取到内存中,并形成一个dom树,如果xml非常大会浪费时间,对内存性能要求高 * @author zhao * @param args */ public static void main(String[] args) { // 创建一个DocumentBuilderFactory的对象 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); try { // 创建一个DocumentBuilder的对象 DocumentBuilder builder = factory.newDocumentBuilder(); // 通过DocumentBuilder对象的parse方法加载bookstroe.xml文件到当前项目下 Document document = builder.parse("bookstore.xml"); // 获取所有book节点的集合 NodeList bookList = document.getElementsByTagName("book"); // 通过NodeList的getLength方法可以获取boolList的长度 System.out.println("===========" + bookList.getLength() + "==========="); // 遍历每一个book节点 for (int i = 0; i < bookList.getLength(); i++) { // 已经知道节点元素的属性名: /* * Element element=(Element) bookList.item(i); String * id=element.getAttribute("id"); System.out.println(id); */ // 不知道节点元素的属性名: // 通过item(i)方法获取一个book节点,NodeList的索引值从0开始 Node book = bookList.item(i); // 获取book节点的所有属性集合 NamedNodeMap map = book.getAttributes(); // 遍历book的属性 System.out.println("第" + (i + 1) + "本书共有" + map.getLength() + "个属性"); for (int j = 0; j < map.getLength(); j++) { // 通过item(j)方法获取book节点的某一个属性 Node attr = map.item(j); // 获取属性名和属性值 System.out.println(attr.getNodeName() + "=" + attr.getNodeValue()); } // 解析book节点的子节点 NodeList childNode = book.getChildNodes(); System.out.println(childNode.getLength()); /* * 通过book元素的getChildNodes()得到所有子节点的集合,需要注意的是,在DOM的读取中, * 会将空格和回车看成是text节点类型,因此需要注意区分开。通过item(index)遍历所有子节点,通过item( * index)的getNodeName()方法还来获取子节点的名字,而获取它的属性值时, * 要注意直接使用getNodeValue()得到的是标签的值(一般是空)因此可以使用两种方法: * 1.使用item(index).getFirstNode().getNodeValue()来返回值(<name>冰与火之歌 * </name>中冰与火之歌是name的子节点,而且这个节点类型是text,现在我们有节点name, * 只需要找到它的第一个节点的节点值便可以了) * 2.使用item(index).getTextContent()来返回属性值 * 上面两种方法的区别在于在一个标签中是否包含的其他的子标签,如果有的话,那么第1个方法就仍得到null, * 第二个方法会返会将自标签的内容一起获得。 */ // 遍历childNode获取每个节点的节点名和节点值 for (int j = 0; j < childNode.getLength(); j++) { Node child = childNode.item(j); // System.out.println(child.getNodeName()); // 区分test类型的node和element类型的node if (child.getNodeType() == Node.ELEMENT_NODE) { System.out.println(child.getNodeName() + "=" + child.getTextContent()); } } } } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
分析:
DOM使用DocumenBuilder的parse方法加载xml文件到当前目录下,parse是分析的意思。首先会用到工厂模式,先生成DocumentBuilderFactory,再由其生成DocumentBuilder,DocumentBuilder.parse会返回一个Document对象,这是xml文件到Document的过程,无论使用DOM ,SAX,JDOM,DOM4J,此步骤大同小异。
Document就是上面写好的xml文件。
常用节点类型 nodeName nodeValue
Element element Name null
Attr 属性名称 属性值
Test #text 节点内容
在此document中,bookstroe是节点,book是节点,name是节点,id也是节点。类似于HTML,document.getElementsByTagName("book")可以得到所有book element,book节点有属性节点Attr,有子节点Element,也有空格Text。Node是所有节点的总称,在未知节点类型时使用Node,所有有book node。
book node的getAttributes返回属性节点的集合NamedNodeMap,进而得到其每个属性节点node,由上述可得属性名称是node.getNodeName,属性值是node.getNodeValue。
book.getChildNodes得到子节点集合,这里我们依旧用Node此时node是element或Text,判断child.getNodeType() == Node.ELEMENT_NODE才是我们需要的子节点,对
<name>冰与火之歌</name>而言,node是name,类型是element,而element的NodeValue却不是冰与火之歌,而是null。冰与火之歌是node的子节点text。故node.getFirstNode().getNodeValue()才是我们需要的结果。
2:SAX
优点:采用事件驱动模式,对内存耗费比较小,适用于只需要处理xml数据时。
缺点:不易编码,很难同时访问同一个xml中的多处不通过数据
package com.zhao.sax; import java.io.IOException; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; public class SAXTest { /** * 应用SAX方式解析XML * sax解析是基于事件的解析,每解析一部分就会触发saxHandler的具体方法 * @author zhao * @param args */ public static void main(String[] args) { // 获取一个SAXParserFactory的实例 SAXParserFactory factory = SAXParserFactory.newInstance(); try { // 通过factory获取SAXParser实例 SAXParser saxParser = factory.newSAXParser(); // 创建SAXHandler对象 SAXHandler handler = new SAXHandler(); saxParser.parse("bookstore.xml", handler); } catch (ParserConfigurationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SAXException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
----------------------
package com.zhao.sax; import java.util.ArrayList; import java.util.List; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import com.zhao.entity.Book; /** * SAXParserHandler类的执行顺序为:startDocument()开始Xml解析--->第一行,以后每行的执行顺序为——> * startElement()---characters()--->endElemnet()---->到最后一行的 * <bookstore>---->endDcunment()。 * * @author zhao * */ public class SAXHandler extends DefaultHandler { private int bookIndex = 0; private Book book; private String value=null; private List<Book> books=new ArrayList<Book>(); /** * 用于标识解析开始 */ @Override public void startDocument() throws SAXException { // TODO Auto-generated method stub super.startDocument(); System.out.println("SAX解析开始"); } /** * 用于标识解析结束 */ @Override public void endDocument() throws SAXException { // TODO Auto-generated method stub super.endDocument(); System.out.println("SAX解析结束"); for (Book book : books) { System.out.println(book); } } /** * 用来遍历xml文件的开始标签 在开始时可用知道属性 */ @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { // 调用DefaultHandler类的startElement方法 super.startElement(uri, localName, qName, attributes); // 开始解析book元素的属性 if ("book".equals(qName)) { bookIndex++; System.out.println(bookIndex + "本书开始"); book=new Book(); // 已知book元素的下属性的名称,根据属性名称获取属性值 // String id=attributes.getValue("id"); // System.out.println("id: "+id); // 不知道book元素下的属性名称,以及个数 for (int i = 0; i < attributes.getLength(); i++) { System.out.println(attributes.getQName(i) + ": " + attributes.getValue(i)); if ("id".equals(attributes.getQName(i))) { book.setId(attributes.getValue(i)); } } } else if (!"book".equals(qName) && !"bookstore".equals(qName)) { System.out.print(qName); } } /** * 用来遍历xml文件的结束标签 */ @Override public void endElement(String uri, String localName, String qName) throws SAXException { // TODO Auto-generated method stub super.endElement(uri, localName, qName); if ("book".equals(qName)) { System.out.println(bookIndex + "本书结束"); books.add(book); book=null; value=null; } else if (!"bookStore".equals(qName)&&!"book".equals(qName)) { if ("name".equals(qName)) { book.setName(value); } else if ("author".equals(qName)) { book.setAuthor(value); } else if("year".equals(qName)){ book.setYear(value); } else if ("price".equals(qName)) { book.setPrice(value); } else if ("language".equals(qName)) { book.setLanguage(value); } } } /** * * @param ch * @param start * @param length * @throws SAXException */ @Override public void characters(char[] ch, int start, int length) throws SAXException { // TODO Auto-generated method stub super.characters(ch, start, length); value = new String(ch, start, length).trim(); if (!value.equals("")) { System.out.println(value); } } }
分析:
SAX使用SAXParser的parse方法解析xml,也是通过SAXParserFactory生成SAXParser。不同于DOMde 节点,sax采用事件驱动,要重写DefaultHandler类,在此类中指明解析xml的过程。
重写DefaultHandler的startDocument endDocument startElement endElement characters方法。
解析顺序为:
startDocument()开始xml解析--》startDocument()开始<bookstore>--》startDocument()开始<book>--》startDocument()开始<name>--》~~~endDocument。
以<>开始,以</>结束,事件驱动,读到标签开始就是startElement,读到标签结束就是endElement。在二者之间是在characters中读内容。
3:JDOM
仅适用具体类而不适用接口,API大量使用了Colletions类
package com.zhao.jdom; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.List; import org.jdom2.Attribute; import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.JDOMException; import org.jdom2.input.SAXBuilder; import com.zhao.entity.Book; public class JDOMTest { /** * 应用JDOM方式解析xml * * @author zhao * @param args */ public static void main(String[] args) { // 准备工作 // 1:创建一个SAXBuilder对象 SAXBuilder saxBuilder = new SAXBuilder(); InputStream inputStream; try { // 创建一个输入流,把xml文件加载到输入流 inputStream = new FileInputStream("bookstore.xml"); InputStreamReader streamReader = new InputStreamReader(inputStream, "UTF-8"); // 通过saxBuilder的build方法,将输入流加载到saxBuilder中 Document doucment = saxBuilder.build(streamReader); // 通过document对象获取xml文件的根节点 Element rootElement = doucment.getRootElement(); // 获取根节点下的子节点的List集合 List<Element> bookList = rootElement.getChildren(); // 继续进行解析 for (Element element : bookList) { Book book = new Book(); System.out.println("开始解析" + (bookList.indexOf(element) + 1)); // Attribute id=element.getAttribute("id"); // System.out.println(id.getName()+" "+id.getValue()); // 解析book的属性 List<Attribute> attributes = element.getAttributes(); for (Attribute attribute : attributes) { System.out.println(attribute.getName() + " " + attribute.getValue()); if ("id".equals(attribute.getName())) { book.setId(attribute.getValue()); } } // 对book节点的子节点的节点名和节点值的遍历 List<Element> bookChilds = element.getChildren(); for (Element child : bookChilds) { System.out.println(child.getName() + " " + child.getValue()); if ("name".equals(child.getName())) { book.setName(child.getValue()); } if ("author".equals(child.getName())) { book.setAuthor(child.getValue()); } if ("year".equals(child.getName())) { book.setYear(child.getValue()); } if ("price".equals(child.getName())) { book.setPrice(child.getValue()); } if ("language".equals(child.getName())) { book.setLanguage(child.getValue()); } } System.out.println(book); } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (JDOMException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
4:DOM4J DOM FOR JAVA
JDOM的一种智能分之,它合并了许多超出基本xml文档表示的功能,使用接口和抽象基本类方法,是一个优秀的Java xml API,具有性能优异,灵活性好,功能强大和极端易用使用的特点。
package com.zhao.dom4j; import java.io.File; import java.util.Iterator; import java.util.List; import org.dom4j.Attribute; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; public class DOM4JTest { /** * 应用DOM4J解析xml * * @author zhao * @param args */ public static void main(String[] args) { // 创建SAXReader的reader SAXReader saxReader = new SAXReader(); try { // 通过reader的read方法加载xml文件,获取document对象 Document document = saxReader.read(new File("bookstore.xml")); // 通过document对象获取根节点bookstore Element bookStore = document.getRootElement(); //通过element对象的elementIterator方法获取迭代器 Iterator iterator = bookStore.elementIterator(); //遍历迭代器,获取根节点中的信息(书籍) while (iterator.hasNext()) { System.out.println("开始遍历"); Element book = (Element) iterator.next(); //获取book的属性名和属性值 List<Attribute> attributes=book.attributes(); for (Attribute attribute : attributes) { System.out.println(attribute.getName()+" "+attribute.getValue()); } // Iterator<Element> iterator2=book.elementIterator(); while (iterator2.hasNext()) { Element element = (Element) iterator2.next(); System.out.println(element.getName()+" "+element.getStringValue()); } } } catch (DocumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }