详解Java中的XML解析
来源:互联网 发布:淘宝首页海报是指什么 编辑:程序博客网 时间:2024/06/05 18:56
详解Java中的XML解析
前言
XML,全称Extensibible Markup Language, 主要用于数据的保存或者文件传输,其主要特性如下所示:
- 以标签为主的标记语言
- 支持自定义标签,支持自我解释
- 与具体技术无关
- 支持验证
- 方便人类的读写
XML示例
为了更好的了解XML,下面我们提供一个简单的XML文件,内容如下所示:
<?xml version="1.0" encoding="UTF-8" ?><!-- 根元素为students 注意XML文件中有且仅有一个根元素--><students> <!-- 子元素student id属性同样可以作为student的子元素 为了演示方便,这里将其作为属性 --> <student id="123"> <!-- student有三个子元素 name、age、gender --> <name>xuhuanfeng</name> <age>22</age> <gender>male</gender> </student> <!--同上--> <student id="456"> <name>Tom</name> <age>23</age> <gender>femal</gender> </student> <!--同上--> <student id="789"> <name>Lily</name> <age>24</age> <gender>femal</gender> </student></students>
在XML中每个元素都可以有子元素/值,元素可以有属性,具体关于XML的内容还请查看官方的文档,接下来的内容主要为Java对XML文件的解析。
XML解析
XML解析主要有两种方式,一种称为DOM解析,另外一种称之为SAX解析。
- DOM解析:Document Object Model,简单的来讲,DOM解析就是读取XML文件,然后在文件文档描述的内容在内存中生成整个文档树。
- SAX解析:Simple API for XML,简单的来讲,SAX是基于事件驱动的流式解析模式,一边去读文件,一边解析文件,在解析的过程并不保存具体的文件内容。
两种解析方式各有千秋,也都有各自的有点和缺点,这里简单罗列如下:
- DOM解析:
- 优点:在内存中形成了整个文档树,有了文档树,就可以随便对文档中任意的节点进行操作(增加节点、删除节点、修改节点信息等),而且由于已经有了整个的文档树,可以实现对任意节点的随机访问。
- 缺点:由于需要在内存中形成文档树,需要消耗的内存比较大,尤其是当文件比较大的时候,消耗的代价还是不容小视的。
- SAX解析:
- 优点:SAX解析由于是一边读取文档一边解析的,所以所占用的内存相对来说比较小。
- 缺点:无法保存文档的信息,无法实现随机访问节点,当文档需要编辑的时候,使用SAX解析就比较麻烦了。
对XML的两种不同解析机制有一定的了解之后,接下来我们就来具体的看下,在Java中是如何解析的。
### DOM解析
关于DOM的解析,这里就不再做过多的解释了,直接通过代码来查看具体的操作过程
解析文档
“`Java
public void parse() {
// students的内容为上面所示XML代码内容
File file = new File(“D:/students.xml”);
try { // 创建文档解析的对象 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); // 解析文档,形成文档树,也就是生成Document对象 Document document = builder.parse(file); // 获得根节点 Element rootElement = document.getDocumentElement(); System.out.printf("Root Element: %s\n", rootElement.getNodeName()); // 获得根节点下的所有子节点 NodeList students = rootElement.getChildNodes(); for (int i = 0; i < students.getLength(); i++){ // 获得第i个子节点 Node childNode = students.item(i); // 由于节点多种类型,而一般我们需要处理的是元素节点 // 元素节点就是非空的子节点,也就是还有孩子的子节点 if (childNode.getNodeType() == Node.ELEMENT_NODE){ Element childElement = (Element)childNode; System.out.printf(" Element: %s\n", childElement.getNodeName()); System.out.printf(" Attribute: id = %s\n", childElement.getAttribute("id")); // 获得第二级子元素 NodeList childNodes = childElement.getChildNodes(); for (int j = 0; j < childNodes.getLength(); j++){ Node child = childNodes.item(j); if (child.getNodeType() == Node.ELEMENT_NODE){ Element eChild = (Element) child; System.out.printf(" sub Element: %s value= %s\n", eChild.getNodeName(), eChild.getTextContent()); } } } } } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }}
“`
解析的结果如下所示:
shell
Root Element: students
Element: student
Attribute: id = 123
sub Element: name value= xuhuanfeng
sub Element: age value= 22
sub Element: gender value= male
# 其余两个student节点由于篇幅原因这里省略...
当我们需要特定的节点的数据的时候,可以根据具体的数据从上面的解析过程中进行数据的筛选即可,所以这里不演示如果进行数据的选取了(毕竟整个文档的内容都读取出来了:))
编辑文档
由于DOM解析是直接在内存中生成对应的文档树,所以我们可以很方便地对其进行编辑,这里演示修改id = 123的子元素name的值为Huanfeng.Xu,具体代码如下所示:
public void modify(){ try { // 生成文档树的过程同前面所示,这里不进行过多的解释 File file = new File("d:/students.xml"); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.parse(file); Element rootElement = document.getDocumentElement(); NodeList students = rootElement.getChildNodes(); for (int i = 0; i < students.getLength(); i++){ Node tmp = students.item(i); if (tmp.getNodeType() == Node.ELEMENT_NODE){ Element element = (Element)tmp; // 获得id为123的student节点 String attr = element.getAttribute("id"); if ("123".equalsIgnoreCase(attr)){ NodeList childNodes = element.getChildNodes(); for (int j = 0; j < childNodes.getLength(); j++){ Node childNode = childNodes.item(j); if (childNode.getNodeType() == Node.ELEMENT_NODE) { Element childElement = (Element) childNode; // 修改子节点name的值 if (childElement.getNodeName().equalsIgnoreCase("name")) { childElement.setTextContent("Huanfeng.Xu"); break; } } } } } } // 获得Transformer对象,用于输出文档 TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); // 封装成DOMResource对象 DOMSource domSource = new DOMSource(document); Result result = new StreamResult("d:/newStudents.xml"); // 输出结果 transformer.transform(domSource, result); } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (TransformerConfigurationException e) { e.printStackTrace(); } catch (TransformerException e) { e.printStackTrace(); }}
可以看到,基本的操作跟解析文档是一致的,这也非常好理解,修改嘛,肯定先要解析文档然后获得需要修改的节点信息,这里同样可以对节点进行删除、增加操作,原理同上,这里就不进行演示。
SAX解析
关于SAX解析的原理,这里就不再做过多的解释,同上面DOM的解析一样,这里我们直接通过代码来查看具体的操作过程
解析文档
/** * 由于SAX解析是基于事件机制的,也就是当遇到指定元素的时候,解析器就会自动调用 * 回调函数,所以使用SAX解析的时候,需要创建自定义的Handler并且继承自DefaultHandler * 并且将其传给解析器,用于指定需要进行回调的内容 */class SAXHandler extends DefaultHandler{ /** * 用于标志是否已经读取到指定的元素 */ private boolean isName; private boolean isAge; private boolean isGender; @Override public void startDocument() throws SAXException { System.out.println("Starting parse the document"); } @Override public void endDocument() throws SAXException { System.out.println("Ending parse the document"); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if ("student".equalsIgnoreCase(qName)){ System.out.println("student"); }else if ("name".equalsIgnoreCase(qName)){ isName = true; }else if ("age".equalsIgnoreCase(qName)){ isAge = true; }else if ("gender".equalsIgnoreCase(qName)){ isGender = true; } } @Override public void characters(char[] ch, int start, int length) throws SAXException { String content = new String(ch, start, length); if (isName){ System.out.printf(" Name: %s\n", content); isName = false; // 这里需要额外注意,当读取到一个节点之后,需要 // 把该节点的标志去除,不然下一次读取会出现问题 }else if (isAge){ System.out.printf(" Age: %s\n", content); isAge = false; }else if (isGender){ System.out.printf(" Gender: %s\n", content); isGender = false; } }}public void parser(){ try { File file = new File("d:/students.xml"); // 创建一个SAX解析器 SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); javax.xml.parsers.SAXParser parser = saxParserFactory.newSAXParser(); // 解析对应的文件 parser.parse(file, new SAXHandler()); } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }}
对应的输出结果如下所示:
studentName: xuhuanfengAge: 22Gender: male# 这里由于篇幅原因,省略其他两个输出内容
由于SAX解析本身不利于节点的保存以及编辑,所以这里就不演示器编辑的过程。
第三方类库解析
上面的内容就是XML解析的最基本的操作了,不过,由于原生API操作不方便,加上效率不怎么高,所以就出现了许多的第三方的解析类库,最常使用的包括了JDOM、StAX、XPath、DOM4j等,下面我们将逐个演示其操作
JDOM解析
JDOM是我们所要接触的第一个第三方解析类库,其操作的原理是基于DOM解析操作,不过JDOM的解析效率比原生操作高,内存占用相对低,使用的时候需要导入JDOM的jar文件,下载地址
解析文档
public void parse(){ try { File file = new File("d:/students.xml"); // 获得一个解析器 SAXBuilder saxBuilder = new SAXBuilder(); Document document = saxBuilder.build(file); // 获得根元素 Element rootElement = document.getRootElement(); System.out.printf("Root Element %s\n", rootElement.getName()); List<Element> elements = rootElement.getChildren(); for (Element e : elements){ System.out.printf(" %s\n", e.getName()); System.out.printf(" Name: %s\n", e.getChild("name").getTextTrim()); System.out.printf(" Age: %s\n", e.getChild("age").getTextTrim()); System.out.printf(" Gender: %s\n", e.getChild("gender").getTextTrim()); } } catch (JDOMException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }}
可以看到使用JDOM进行解析是比较方便的,而且由于JDOM使用了List等容器类,更加方便操作了。
StAX解析
StAx是我们要使用的第二个第三方解析类库,StAX的实现原理为SAX操作,不过StAX提供了比原生SAX解析更加方便的操作,使用时同样需要导入其jar文件,下载地址
解析文档
public void parse(){ boolean isName = false; boolean isAge = false; boolean isGender = false; try { File file = new File("d:/students.xml"); // 获得解析器 XMLInputFactory factory = XMLInputFactory.newFactory(); XMLEventReader reader = factory.createXMLEventReader(new FileReader(file)); while (reader.hasNext()){ // 获得事件 XMLEvent event = reader.nextEvent(); switch (event.getEventType()){ // 解析事件的类型 case XMLStreamConstants.START_ELEMENT: StartElement startElement = event.asStartElement(); String qName = startElement.getName().getLocalPart(); if ("name".equalsIgnoreCase(qName)){ isName = true; }else if ("age".equalsIgnoreCase(qName)){ isAge = true; }else if ("gender".equalsIgnoreCase(qName)){ isGender = true; } break; case XMLStreamConstants.CHARACTERS: Characters characters = event.asCharacters(); if (isName){ System.out.printf(" Name: %s\n", characters.getData()); isName = false; }else if (isAge){ System.out.printf(" Age: %s\n", characters.getData()); isAge = false; }else if (isGender){ System.out.printf(" Gender: %s\n", characters.getData()); isGender = false; } break; } } } catch (XMLStreamException e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); }}
XPath
XPath从严格意义上来讲并不是一种解析方式,不过XPath提供了一种定位节点的方式,XPath表达式,通过该表达式,我们可以定位到指定特性的一个或者一组节点
常用的XPath表达式如下所示:
/ :从根节点开始查找
//:从当前节点开始查找
. :选择当前节点
..:选择当前节点的父节点
@:指定元素
还有其他一些表达式,可以参考XPath表达式
解析文档
public void parse(){ try { File file = new File("d:/students.xml"); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); // 创建xpath对象 XPath xPath = XPathFactory.newInstance().newXPath(); Document document = builder.parse(file); // 编写xpath表达式 String expression = "/students/student"; NodeList students = (NodeList)xPath.compile(expression).evaluate(document, XPathConstants.NODESET); for (int i = 0; i < students.getLength(); i++){ Node node = students.item(i); if (node.getNodeType() == Node.ELEMENT_NODE){ Element element = (Element) node; System.out.printf(" Element: %s\n", element.getNodeName()); System.out.printf(" Name: %s\n", element.getElementsByTagName("name").item(0).getTextContent()); System.out.printf(" Age: %s\n", element.getElementsByTagName("age").item(0).getTextContent()); System.out.printf(" Gender: %s\n", element.getElementsByTagName("gender").item(0).getTextContent()); } } } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (XPathExpressionException e) { e.printStackTrace(); } }
可以看到,使用XPath技术本质上还是使用DOM解析,只不过借助XPath表达式,可以很方便地定位到指定元素
DOM4J解析
DOM4J是一个比较优秀的解析类库,也是目前使用得比较多的库类,使用的时候可以配合XPath技术来辅助定位某一个节点,使用的时候需要导入对应的jar文件,下载地址,注意使用DOM4J的时候需要导入两个jar文件,DOM4J本身的jar文件以及jaxen文件
解析文档
public void parse() throws DocumentException { File file = new File("d:/students.xml"); // 加载文档 SAXReader reader = new SAXReader(); Document document = reader.read(file); Element rootElement = document.getRootElement(); System.out.printf("Root Element: %s\n", rootElement.getName()); // 使用XPath表达式来定位节点 List<Node> students = document.selectNodes("/students/student"); for (Node n: students){ System.out.printf("Element: %s\n", n.getName()); System.out.printf("Name: %s\n", n.selectSingleNode("name").getText()); System.out.printf("Age: %s\n", n.selectSingleNode("age").getText()); System.out.printf("Gender: %s\n", n.selectSingleNode("gender").getText()); } }
可以看到,使用DOM4J解析文档是非常方便的,不仅如此,使用DOM4J生成文档也是非常方便的
生成文档
public void create() throws IOException { Document document = DocumentHelper.createDocument(); Element root = document.addElement("students"); Element student = root.addElement("student"); student.addElement("name") .addText("xuhuanfeng"); student.addElement("age") .addText("22"); OutputFormat format = OutputFormat.createPrettyPrint(); XMLWriter writer = new XMLWriter(System.out); writer.write(document);}
总结
本节我们学习了XML解析的机制,包括了DOM解析以及SAX解析,并且通过具体实例使用不同解析技术进行解析,还了解了几个常用的XML解析类库,包括了JDOM、StAX、XPath、DOM4J等,并且通过具体操作更加具体地了解了其操作的过程。
- 详解Java中的XML解析
- java中的XML解析
- java中的xml解析
- Java中的解析Xml
- Java中的XML解析
- java中的XML解析
- IOS中的XML解析详解
- Java DOM 解析 XML详解
- Java Dom4j解析XML详解
- Java DOM 解析 XML详解
- Java DOM 解析 XML详解
- Java DOM 解析 XML详解
- Java DOM 解析 XML详解
- java-Dom4j解析XML详解
- xml在java中的解析
- Java中的XML文件解析
- java中的xml 解析xml(上)
- java中的xml,解析xml(下)
- zTree简单使用和代码结构
- [机器学习]week3编程作业:Logistic Regression
- 离散题目7
- HDOJ 1024 Max Sum Plus Plus
- Android Shape 的使用
- 详解Java中的XML解析
- Android相机基础介绍
- 面试感悟
- UI自动测试中遇到
- 安卓 身份证键盘 输入限制
- HDU1176(二维动态规划)
- Activity的详解
- java基础--if和switch的区别
- intel joule 500系列开发板学习笔记