1、xml是标记型文档,而js使用dom解析标记型文档,解析原理:根据html的层级结构,在内存中分配一个树形结构,把html的标签】属性和文本都封装成对象,分别为:document对象、element对象、属性对象、文本duix,Node节点对象。xml的解析方式(技术):dom和sax
2、使用dom方式解析时,如果文件过大,会造成内存溢出,优点是易于实现增删改操作。
sax解析过程:采用事件驱动,边读边接续:从上到下,一行行解析,解析到某个对象,就把对象名称返回。使用sax方式实现查询不会造成内存溢出,但不能实现增删改操作。
3、通过API方式提供的解析器
sun公司,提供的针对dom和sax方式的解析器:jaxp
dom4j组织,提供的针对dom和sax方式的解析器:dom4j(在实际开发中使用最多)
jdom组织,提供的针对dom和sax方式的解析器:jdom
4、jaxp的API查看
>jaxp是JavaSE的一部分,在:
Module java.xml
Package javax.xml.parsers
中,其中四个类分别是针对dom和sax解析使用的类:
dom:
public abstract class DocumentBuilder extends Object解析器类
public abstract class DocumentBuilderFactory extends Object解析工厂
>抽象类不能通过new关键字实例化,所以其实例通过解析工厂中方法获取:public abstract DocumentBuilder newDocumentBuilder() throws ParserConfigurationException
而解析工厂类的实例则通过可中的静态方法获取:public static DocumentBuilderFactory newInstance()
>在解析器类中获取待解析文件的方法:(接收文件、字符串路径、字符流)
public Document parse(File f) throws SAXException,IOException
注意返回的Document是一个接口,父接口是Node(public interface Document extends Node)
Document接口中的方法:得到标签、创建标签、创建文本、把文本添加到标签下面、删除节点等方法,例:
NodeList getElementsByTagName(String tagname)
该方法返回值是NodeList型,而NodeList接口中有两个方法:一个是getLength()获取集合长度,另一个是item(int index)方法,根据索引值获取集合中项元素
>对xml文件进行修改操作后,需要回写,否则在原文件中查看不到:通过回写工厂类实例化的回写类:TransformerFactory tfactory = TransformerFactory.newInstance();
回写类中的回写方法:public abstract void transform(Source xmlSource,Result outputTarget) throws TransformerException
其参数类型:public interface Source,其实现类之一:DOMSource类,构造方法可接收Node类型参数。
代码示例:
person.xml
<?xml version="1.0" encoding="UTF-8"?>
<person>
<p1>
<name>zhangsan</name>
<age>16</age>
</p1>
<p1>
<name>lisi</name>
<age>20</age>
</p1>
<p1>
<name>wangwu</name>
<age>19</age>
</p1>
</person>
PersonTestJaxp.java
import java.io.IOException;import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.SAXException;
/**
* 实现jaxp操作xml
* @author 17864
*/
public class PersonTestJaxp {
public static void main(String[] args) throws Exception {
selectAll() ;
selectSin() ;
addSex() ;
modifyAge() ;
// deleteAge() ;
listElement() ;
}
//查询所有name元素的值
private static void selectAll() throws ParserConfigurationException, SAXException, IOException {
//1.创建解析工厂 alt+/ 代码提示
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance() ;
//2.创建解析器
DocumentBuilder db = dbf.newDocumentBuilder() ;
//3.解析xml返回document(注意包w3c)
Document d = db.parse("src/person.xml") ;
//4.得到name元素
NodeList list = d.getElementsByTagName("name") ;
//5.遍历集合
for(int i = 0 ; i < list.getLength() ; i ++) {
//得到每一个name元素,Node类型
Node name = list.item(i) ;
//得到name元素中的值,String类型,输出快捷键 syso+alt+/
System.out.println(name.getTextContent()) ;
}
}
//获取第一个name元素的值
public static void selectSin() throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance() ;
DocumentBuilder db = dbf.newDocumentBuilder() ;
Document d = db.parse("src/person.xml") ;
NodeList list = d.getElementsByTagName("name") ;
System.out.println("获取第一个name元素的值:" + list.item(0).getTextContent()) ;
}
//使用JAXP添加节点——对xml文件进行修改操作后,需要回写,否则在原文件中查看不到
public static void addSex() throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance() ;
DocumentBuilder db = dbf.newDocumentBuilder() ;
Document d = db.parse("src/person.xml") ;
//得到第一个p1
Node p1 = d.getElementsByTagName("p1").item(0) ;
//创建sex标签
Element sex = d.createElement("sex") ;
//创建文本
Text text = d.createTextNode("female") ; //先创建再添加
//把文本添加到标签下
sex.appendChild(text) ;
//把标签添加到p1下
p1.appendChild(sex) ;
//实例化回写对象
TransformerFactory tff = TransformerFactory.newInstance() ;
Transformer tf = tff.newTransformer() ;
//回写方法:public abstract void transform(Source xmlSource,Result outputTarget) throws TransformerException参数是接口类型,要找到其实现类传参
tf.transform(new DOMSource(d), new StreamResult("src/person.xml"));
}
//修改第一个p1下面的age为100
public static void modifyAge() throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance() ;
DocumentBuilder db = dbf.newDocumentBuilder() ;
Document d = db.parse("src/person.xml") ;
Node age = d.getElementsByTagName("age").item(0) ;
age.setTextContent("100");
TransformerFactory tff = TransformerFactory.newInstance() ;
Transformer tf = tff.newTransformer() ;
tf.transform(new DOMSource(d), new StreamResult("src/person.xml"));
}
//删除第二个p1下的age节点
public static void deleteAge() throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance() ;
DocumentBuilder db = dbf.newDocumentBuilder() ;
Document d = db.parse("src/person.xml") ;
Node age = d.getElementsByTagName("age").item(1) ;
Node p1 = age.getParentNode() ; //删除节点时必须找到其父节点
p1.removeChild(age) ;
TransformerFactory tff = TransformerFactory.newInstance() ;
Transformer tf = tff.newTransformer() ;
tf.transform(new DOMSource(d), new StreamResult("src/person.xml"));
}
//遍历节点
public static void listElement() throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance() ;
DocumentBuilder db = dbf.newDocumentBuilder() ;
Document d = db.parse("src/person.xml") ;
list(d) ;
}
private static void list(Node node) { //参数向上转型
//判断节点是否为元素类型,是,打印,否则,会将空格换行等内容全部都打印输出
if(node.getNodeType() == Node.ELEMENT_NODE) {
System.out.println(node.getNodeName()) ;
}
//得到一层子节点
NodeList list = node.getChildNodes() ;
//遍历list
for(int i = 0 ; i < list.getLength() ; i ++) {
Node node1 = list.item(i) ;
list(node1) ; //递归
}
}
}
5、sax解析
>sax是JavaSE的一部分,在:
Module java.xml
Package javax.xml.parsers
中,其中四个类分别是针对dom和sax解析使用的类,支持sax的两个类:
public abstract class SAXParser extends Object解析器类
public abstract class SAXParserFactory extends Object解析工厂
>SAXParser类实例通过SAXParserFactory.newSAXParser()方法获得。类中解析方法:
public void parse(File f,DefaultHandler dh) throws SAXException, IOException第一个参数是xml路径,第二个参数是事件处理器
当遇到不同类型标签时sax解析器会自动执行不同的方法:
当解析到开始标签时,执行:其中qName返回标签名
public void startElement(String uri,
String localName,
String qName,
Attributes attributes)
throws SAXException
解析到文本时执行:
public void characters(char[] ch,
int start,
int length)
throws SAXException
解析到结束标签时执行:
public void endElement(String uri,
String localName,
String qName)
throws SAXException
>使用schema的sax方式解析xml(sax方式解析只能做查询操作,不能实现增删改)
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;
public class TestSax {
public static void main(String[] args) throws Exception {
SAXParserFactory sf = SAXParserFactory.newInstance() ;
SAXParser s = sf.newSAXParser() ;
s.parse("src/person.xml", new MyDefault()); //事件处理器需要自己创建一个类,继承DefaultHandler,在类中重写三个方法
}
}
class MyDefault extends DefaultHandler {
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
System.out.print("<" + qName + ">");
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
System.out.print(new String(ch,start,length));
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
System.out.print("</" + qName + ">");
}
}
//获取所有的name元素值
class MyDefault2 extends DefaultHandler {
boolean flag = false ;
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
//判断qName是否是name元素
if("name".equals(qName)) {
flag = true ;
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
//当flag值是true时表示解析到name元素
if(flag == true) {
System.out.println(new String(ch,start,length));
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
//把flag设置成false,表示name元素结束
if("name".equals(qName)) {
flag = false ;
}
}
}
6、使用dom4j解析器(性能比jdom好)解析xml
>dom4j不是JavaSE的一部分,所以使用dom4j需要导入jar包(dom4j-1.6.1.jar)
导包:将jar包粘贴入lib文件夹(新建Folder)下,然后选中jar包→右键选择Build Path→Add to Build Path
>得到document:
SAXReader reader = new SAXPeader();
Document document = reader.read(url);
document的父接口是Node,其中有方法:
获取根节点getElement(),返回Element
Element也是一个接口,父接口是Node,其中有方法:
获取父节点:getParent();
直接在父节点下添加标签:addElement(String ele);
直接在标签后添加文本内容:setText(String text);
回写操作:
OutputFormat format = OutputFormat.createPrettyPrint() ; //格式化OutputFormat,有缩进效果
XMLWriter xmlWriter = XMLWriter(new FileOutputStream("src/person.xml"), format) ;
xmlWriter.write(document) ;
xmlWriter.close() ; //关闭流
>使用dom4j查询xml
//查询xml中所有name元素的值
public static void selectName() {
//创建解析器
SAXReader saxReader = new SAXReader();
//得到document
Document document = saxReader.read(url);
//得到根节点
Element root = document.getRootElement() ;
//得到p1标签
/*
* 得到标签:
* element(qName):表示获取标签下面的指定子标签,如p1标签下的name标签
* elements(qName):表示获取标签下面所有指定子标签(一层),如得到所有的根标签下的p1标签,返回一个list集合,获取List结合中元素使用get(int index)方法
* elements():获取标签下面的所有一层子标签
* getText()得到元素中的文本内容
*/
List<Element> list = root.elements("p1") ;
//遍历list
for(Element element : list) {
//得到p1下面的name元素
Element name = element.element("name") ;
//得到name元素中值输出
System.out.println(name.getText());
}
}
>在指定位置添加节点的核心代码:
创建元素:Element school = DocumentHelper.createElement("school") ;
给新创建的元素设置文本内容:school.setText("ecit") ;
获取根元素下第一个p1元素下的所有元素:List<Element> list = p1.elements() ;
在特定位置添加元素:list.add(1,school);
回写
注:可以对得到document的操作和回写操作封装成方法。还可以将要传递的文件路径封装成静态常量。
>使用dom4j实现修改操作的核心代码:得到元素后直接setText(String str)
>使用dom4j实现删除操作的核心代码:
删除也是需通过父节点删除:
知道根节点p1是school标签的父节点:p1.remove(sch); //sch是获取到的school元素Element sch = p1.element("school");
需要获取标签的父节点:Element p = sch.getParent();然后删除
回写
>使用dom4j获取属性的核心代码:
得到标签元素后,根据标签元素的属性名称获取属性值p1.attributeValue("id1")
7、使用dom4j支持XPATH操作:可以直接获取到某个元素,不用一层层解析
>使用形式:
/A/B/C:表示层级,A标签下的B标签下的C标签
//B:表示标签名为B的所有标签
/*:表示所有标签
B[1]:表示第一个B标签
B[last()]:表示左后一个B标签
//B[@id]:表示所有有id属性的B标签
//B[@id='b1']:表示所有有id属性且属性值为b1的B标签
>默认情况下dom4j是不支持xpath的,若要使用则需要导入jar包:jaxen-1.1-beta-6.jar
dom4j中提供的两个支持xpath的方法:
获取多个节点:List<Node> selectNodes("xpath表达式")
获取单个节点:Node selectSingleNode("xpath表达式")
(获取节点元素中文本内容通过getText()方法)