最近学习了xml入门和解析, 为了加深理解特地整理了一下相关笔记.
1. HTML&XML
- html和xml都属于SGML(标准通用标记语言)语言分支
- 程序型标记-HTML(HTML5不属于SGML分支)
- 描述型标记-XML(1998正式形成标准)
- 两个都由W3C维护
2 . XML入门
extendsiable markup language, 可扩展标记语言, xml只代表数据本身, 而不包含任何样式结构的呈现, 所以也称之为一种数据描述语言.
3 . XML作用
- 实现不同平台之间的数据交换(webservice:soap协议)
- xml还可以用于一些应用程序的配置文件(框架, tomcat, servlet)
4 . XML文档构成
一个标准的XML文档由3部分构成.
1 . xml指令
xml指令主要描述xml的版本(目前只有1.0), 编码, 文档类型定义是否是一个独立的文件.
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
2 . 文档类型定义(通常可不加)
文档类型定义(DTD,XSD): 规范文档中允许出现的标记, 属性, 以及标记之间的关系.
<!DOCTYPE>
3 . 文档元素部分
文档的详细构成: 标签, 属性, 文本.
- xml元素: 由一对标签以及标签之间的内容构成.
- xml标签: 主要包含开始标记以及结束标记.
- xml属性: 通常定义在元素的开始标记中.
5 . XML规范
XML规范相对HTML来说相当严格.
1. 标记必须成对出现(除空标记,即单标签).
2. 标签严格区分大小写.
3. 属性值必须使用双引号""来包含.
4. 属性值中不能包含特殊符号(<, &, …).
5. xml支持类似html中实体(<(& lt;), >(& gt;), "(& quot;))
6. 注释代码不允许嵌套
7. 一个xml文档中有且只有一对根节点.
元数据
<![CDATA[
元数据具体内容.
]]>
<languges language="java">
<code>
<![CDATA[
public static void main(String[] args)
{
}
]]>
</code>
</languges>
命名空间: namespace, 防止标签命名冲突.
<h:table></h:table>
<f:table></f:table>
6 . JSON&XML
AJAX: 异步的 javascript and XML
JSON作用: 是一种轻量级的数据交换格式, 可以实现不同平台之间的数据交换.
XML作用: 上面已述(第3条).
7. XML解析
1. DOM解析:
将需要被解析的文档完整的加载到内存中(有大小限制, 无法解析大文件), 解析为一颗倒置的文档树, 可以通过解析器任意获取文档.
优点: 适合解析较小的文档, 解析速度很快, 可以任意搜索节点, 并行搜索.
缺点: 一次性加载整个文档,会消耗大量内存, 一般无法解析过大的文件(和内存有关).
package com.softeem.xml.parse;
import java.io.File;
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.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public class ParseByDOM {
public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
//实例化一个解析工厂
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
//获取解析器对象
DocumentBuilder builder = factory.newDocumentBuilder();
//解析文件并获取文档对象
Document document = builder.parse(new File("src/userInfo.xml"));
//根据元素的标签名获取元素并取出匹配第一个元素(获取文档根节点)
Node root = document.getElementsByTagName("users").item(0);
//获取当前节点下的所有子节点
NodeList list = root.getChildNodes();
for (int i = 0; i < list.getLength(); i++) {
Node node = list.item(i);
//判断当前节点名称是否为user
if("user".equals(node.getNodeName())){
//获取节点中的属性节点集合
NamedNodeMap nnm = node.getAttributes();
//根据节点名称(属性名)获取节点值(属性值)
String id = nnm.getNamedItem("id").getNodeValue();
String type = nnm.getNamedItem("type").getNodeValue();
System.out.println(id);
System.out.println(type);
//继续搜索user节点下的子节点
NodeList user_child_list = node.getChildNodes();
for(int j=0;i<user_child_list.getLength();j++){
Node c = user_child_list.item(j);
if(c != null){
if("name".equals(c.getNodeName())){
String name = c.getTextContent();
System.out.println("姓名:"+name);
}else if("sex".equals(c.getNodeName())){
String sex = c.getTextContent();
System.out.println("性别:"+sex);
}else if("age".equals(c.getNodeName())){
String age = c.getTextContent();
System.out.println("年龄:"+age);
}else if("ismarray".equals(c.getNodeName())){
String ismarray = c.getTextContent();
System.out.println("婚否:"+ismarray);
}
}
}
}
}
}
}
userInfo.xml文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<!-- <!DOCTYPE users[ -->
<!-- <!ELEMENT users (user)> -->
<!-- <!ELEMENT user (name,sex,age,ismarray)> -->
<!-- <!ELEMENT name (#PCDATA)> -->
<!-- <!ELEMENT sex (#PCDATA)> -->
<!-- <!ELEMENT age (#PCDATA)> -->
<!-- <!ELEMENT ismarray (#PCDATA)> -->
<!-- <!ATTLIST user id(#CDATA)> -->
<!-- ]> -->
<users>
<user id="201709001" type="admin">
<name>二蛋</name>
<sex>男</sex>
<age>18</age>
<ismarray>未婚</ismarray>
</user>
</users>
2 . SAX解析:
基于事件驱动的方式以类似流媒体的方式进行解析, 在读取到一部分后立即开始解析,直到读取到文档结束标记后停止解析.
优点: 适合解析较大的文档, 解析效率较快, 一边读取一边解析.
缺点: 无法任意的搜索节点, 比较难以实现并行搜索.
使用方法: 该类要继承DefaultHandler, 之后重写5个方法(start/endDocument, characters, start/endElement).
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import com.softeem.xml.dto.Book;
public class ParseBySAX extends DefaultHandler{
public List<Book> books;
private Book book;
private String nowTag;//记录当前读取的标记
//文档开始时触发
@Override
public void startDocument() throws SAXException {
System.out.println("开始解析");
books = new ArrayList<Book>();
}
//读取到开始标记时触发
/**
* @param uri 命名空间的uri地址
* @param localName 不带命名空间前缀的标签名称
* @param qName 包含命名空间的标签名
* @param attributes 元素中的属性列表
*/
@Override
public void startElement(String uri, String localName, String qName, Attributes attr) throws SAXException {
System.out.println("读取到开始标记:"+qName);
//记录当前读取到的开始标记
nowTag = qName;
//当读取到一个开始的book标记后创建一个book对象
if("book".equals(qName)){
book = new Book();
book.setNo(attr.getValue("no"));
}
}
//读取到文本节点时触发
@Override
public void characters(char[] c, int start, int length) throws SAXException {
System.out.println("读取到文本节点:"+new String(c,start,length));
String value = new String(c,start,length);
//根据当前记录的标记决定将文本值设置给对象的哪一个属性
if("name".equals(nowTag)){
book.setName(value);
}else if("author".equals(nowTag)){
book.setAuthor(value);
}else if("price".equals(nowTag)){
book.setPrice(value);
}else if("publish".equals(nowTag)){
book.setPublish(value);
}
//将节点置为空(重要)
//nowTag = "";
}
//读取到结束标记时触发
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
System.out.println("读取到结束标记:"+qName);
//判断是否读取到book结束
if("book".equals(qName)){
books.add(book);
}
}
//文档结束时触发
@Override
public void endDocument() throws SAXException {
System.out.println("解析完成");
}
public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
//创建解析器工厂
SAXParserFactory factory = SAXParserFactory.newInstance();
//生产解析器
SAXParser parse = factory.newSAXParser();
ParseBySAX pbs = new ParseBySAX();
//开始解析
parse.parse("src/book.xml",pbs);
List<Book> list = pbs.books;
System.out.println(list);
}
}
book.xml文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<books>
<book no="0103410">
<name>还珠格格</name>
<author>琼瑶</author>
<publish>芒果台出版社</publish>
<price>34.50</price>
</book>
<book no="0810276">
<name>神雕侠侣</name>
<author>金庸</author>
<publish>北京大学出版社</publish>
<price>66.48</price>
</book>
<book no="0911239">
<name>疯狂java讲义</name>
<author>李刚</author>
<publish>电子工业出版社</publish>
<price>99.7</price>
</book>
</books>
3. JDOM解析:
java-base Document Object Model
性能比较差, 但使用比较简单.
使用方法:
需要导入一个jar包(jdom.jar)
package com.softeem.xml.parse;
import java.io.File;
import java.io.IOException;
import java.util.List;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
/**
* JDOM(Java-based Document Object Model)
* @author mrchai
*/
public class ParseByJDOM {
public static void main(String[] args) throws JDOMException, IOException {
//创建解析器
SAXBuilder builder = new SAXBuilder();
//解析指定文档为一个Document对象
Document document = builder.build(new File("src/book.xml"));
//获取文档的根节点
Element root = document.getRootElement();
//获取根节点下所有的
List<Element> list = root.getChildren("book");
//遍历所有的book节点
for (Element e : list) {
//取出节点中指定属性值
String no = e.getAttributeValue("no");
//获取当前节点下指定名称的单个子节点,并去到节点中的文本值
String name = e.getChild("name").getText();
String author = e.getChild("author").getText();
String publish = e.getChild("publish").getText();
String price = e.getChild("price").getText();
System.out.println(no);
System.out.println(author);
System.out.println(name);
System.out.println(publish);
System.out.println(price);
System.out.println("============");
}
}
}
4. DOM4J解析:
document object model for java.
使用时需要导包: dom4j.jar
支持Xpath技术: 快速搜索节点(很方便).
需要导包: jaxen.jar
使用方法:
package com.softeem.xml.parse;
import java.io.File;
import java.util.List;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
/**
* dom4j:document object model for java
*
* log4j
* slf4j
* pinyin4j
*
* @author mrchai
*
*/
public class ParseByDOM4J {
public static void main(String[] args) throws DocumentException {
//创建一个dom4j解析器
SAXReader reader = new SAXReader();
//解析文档
Document document = reader.read(new File("src/book.xml"));
//获取根节点
Element root = document.getRootElement();
//获取当前根节点下指定名称的子节点集合
List<Element> list = root.elements("book");
for (Element e : list) {
//获取当前元素的no属性值
String no = e.attributeValue("no");
String name = e.element("name").getText();
String author = e.element("author").getText();
String price = e.element("price").getText();
String publish = e.element("publish").getText();
System.out.println(no);
System.out.println(name);
System.out.println(author);
System.out.println(price);
System.out.println(publish);
}
}
}
使用Xpath技术:
package com.softeem.xml.parse;
import java.io.File;
import java.util.List;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
public class ParseByDOM4J_XPath {
public static void main(String[] args) throws DocumentException {
//获取解析器
SAXReader reader = new SAXReader();
//解析文件获取文档对象
Document document = reader.read(new File("src/book.xml"));
//快速搜索books/book/name节点
List<Node> list = document.selectNodes("books/book/name");
for (Node node : list) {
String qname = node.getName();
String name = node.getText();
System.out.println(qname+"==="+name);
}
List<Node> list2 = document.selectNodes("books/book");
for (Node node : list2) {
//获取节点的指定属性值
String no = node.valueOf("@no");
System.out.println("--->"+no);
}
}
}