java操作xml简介
- java里面操作xml的方法无非就是读与写,读出来才能谈后续的替换、查找等操作,java里面操作xml的方法也是五花八门的,我这里总结几种常用的方法,见下图:
xml读:
sax,dom,jdom,dom4j
xml写:
dom,jdom,dom4j,xstream
sax:
- 即Simple API for XML,它是java本身自带的类库,使用它不需要你引入任何其他的jar包,在xml的解析方面性能特别快,特别针对比较大的xml文档,处理起来很有优势。
dom:
- 即jdk针对w3c dom结构标准的实现,虽然可以用来xml的解析与写入,不过在实际使用的状况来看,还是在写入方面使用的比较多,解析建议使用sax。
jdom:
- 是一个第三方类库,我们使用的使用引入jdom.jar就可以使用了
dom4j:
- "4j"这个词不读做“四j”,而是读作“for j”,意思是dom for java,也是一个第三方类库,我们使用时也需要引入它的jar包。
SAX方式
- Java JDK自带的解析(SAXParserFactory SAXPaeser DefaultHandler)
- 特点:基于事件模型的,一行一行的往下面执行解析的
- startDocment startelement characters endElement endDocment
SAX采用事件处理的方式解析XML文件,利用 SAX 解析 XML 文档,
涉及两个部分:解析器 和 事件处理器
- 解析器可以使用JAXP的API创建,创建出SAX解析器后,就可以指定解析器去解析某个XML文档。
- 解析器采用SAX方式在解析某个XML文档时,它只要解析到XML文档的一个组成部分,都会去调用事件处理器的一个方法,解析器在调用事件处理器的方法时,会把当前解析到的xml文件内容作为方法的参数传递给事件处理器。
- 事件处理器由程序员编写,程序员通过事件处理器中方法的参数,就可以很轻松地得到sax解析器解析到的数据,从而可以决定如何对数据进行处理。
备注说明:SAX API中主要有四种处理事件的接口,它们分别是ContentHandler,DTDHandler, EntityResolver 和 ErrorHandler
解析步骤:
-
创建一个SAXParserFactory对象
SAXParserFactory factory=SAXParserFactory.newInstance();
-
获得解析器
SAXParser parser=factory.newSAXParser();
-
调用解析方法解析xml
这里的第一个参数可以传递文件、流、字符串、需要注意第二个参数
(new DefaultHander) File file=new File(“girls.xml”);
parser.parse(file,new DefaultHandler());
/**注解:—>这里的DefaultHandler表示
DefaultHandler类是SAX2事件处理程序的默认基类。它继承了EntityResolver、DTDHandler、
ContentHandler和ErrorHandler这四个接口。包含这四个接口的所有方法,所以我们在编写事件处理程序时,
可以不用直接实现这四个接口,而继承该类,然后重写我们需要的方法,所以在这之前我们先定义一个用于实现解析
方法如下:*/
-
创建一个MyHandler类来继承DefaultHandler并重写方法
-
MyHandler extends DefaultHander
-
重写方法,快速记住方法(2个开始,2个结束,1一个文字(charactor–里面的内容))
-
2个开始:StartDocment(文档的开始)StartElement(元素的开始) 2个结束:endElement(元素的结束) endDocment(文档的结束,标志着xml文件的结束) 1个文字内容:charactor(文字内容)
-
-
创建一个集合把所解析的内容添加到集合
分析:目的我们只是需要把xml里面的文字内容添加到我们的集合而不需要其他元素,所以我们需要进行判断得到 -
接步骤三 输出集合System.out.pritnln(list); 解析完成!
DefaultHandler重写的几个方法:
- startDocument():文档解析开始时调用,该方法只会调用一次
- startElement(String uri, String localName, String qName, Attributes attributes):标签(节点)解析开始时调用
- uri:xml文档的命名空间
- localName:标签的名字
- qName:带命名空间的标签的名字
- attributes:标签的属性集
- characters(char[] ch, int start, int length):解析标签的内容的时候调用
- ch:当前读取到的TextNode(文本节点)的字节数组
- start:字节开始的位置,为0则读取全部
- ength:当前TextNode的长度
- endElement(String uri, String localName, String qName):标签(节点)解析结束后调用
- endDocument():文档解析结束后调用,该方法只会调用一次
记住这张图:
案例 1:
- 我们将对这个xml文件进行SAX解析
第一步 :初始化一个Girl类:
public class Girl {
private String id;
private String name;
private String age;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
@Override
public String toString() {
return "Girl [name=" + name + ", age=" + age + "]";
}
}
第二步:编辑执行器Handler
继承DefaultHandler:
import com.cy.pojo.Girl;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import java.util.ArrayList;
import java.util.List;
public class MyHandler extends DefaultHandler {
private List<Girl> girls;
private Girl girl;
private String tag;
public List<Girl> getGirls() {
return girls;
}
public void setGirls(List<Girl> girls) {
this.girls = girls;
}
public Girl getGirl() {
return girl;
}
public void setGirl(Girl girl) {
this.girl = girl;
}
public String getTag() {
return tag;
}
public void setTag(String tag) {
this.tag = tag;
}
@Override
public void startDocument() throws SAXException {
// 因为这个方法只调用一次,所以在开始的时候就可以实例化集合
System.out.println("sax解析开始");
girls=new ArrayList<>();
}
@Override
public void endDocument() throws SAXException {
super.endDocument();
System.out.println("sax解析结束");
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
// 这个方法,只有当开始一个元素的时候才会调用,
// 通过分析,当外部开始元素为girl的时候,需要将girl实例化
// 将tag赋值
tag=qName;
if ("girl".equals(qName)) {
girl=new Girl();
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
// 这句话,必须写,因为,当sax解析完一个元素的时候,会自动认为换行符是一个字符,会继续执行 character 方法 。如果不写,就会造成没有数据的现象。
tag="";
// 这个方法,当到了元素结尾的时候,会调用,应该在这里,将对象添加到集合里面去。
if ("girl".equals(qName)) {
girls.add(girl);
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
// 这里是内容,但是,无法直接判断属于哪一个元素。
String string = new String(ch, start, length);
if ("name".equals(tag)) {//判断当前内容,属于哪一个元素。
girl.setName(string);
}else if ("age".equals(tag)) {
girl.setAge(string);
}//这两种情况,表示 当前语句执行在 girls 标签内。
}
}
第三步: 解析
public class Sax03 {
public static void main(String[] args) {
// 1.创建对象
SAXParserFactory newInstance = SAXParserFactory.newInstance();
try {
// 2.获取解析器
SAXParser saxParser = newInstance.newSAXParser();
// 3.调用方法开始解析xml
File file = new File("girls.xml");
MyHandler dh = new MyHandler();
saxParser.parse(file, dh);
List<Girl> girls=dh.getGirls();
// 4.输出集合
System.out.println(girls);
} catch (ParserConfigurationException | SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
案例 2:
要解析的msg.xml:
需求:
不让你找到optionName标签内的文本内容了,直接让你读出来整个文档
编辑执行器Handler
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
public class XmlHandler extends DefaultHandler {
@Override
public void startDocument() throws SAXException {
System.out.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
}
@Override
public void endDocument() throws SAXException {
System.out.print("解析完毕");
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
System.out.print("<");
System.out.print(qName);
if(attributes!=null){
for (int i = 0;i<attributes.getLength(); i++){
System.out.print(" "+attributes.getQName(i)+"=\""+attributes.getValue(i)+"\"");
}
}
System.out.print(">");
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
System.out.print("</");
System.out.print(qName);
if (qName.equals("message")){
System.out.println(">");
}else {
System.out.print(">");
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
System.out.print(new String(ch,start,length));
}
}
解析:
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.File;
public class XmlReader2 {
public static void main(String[] args) throws Exception {
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
SAXParser saxParser = saxParserFactory.newSAXParser();
File file = new File("src\\main\\resources\\mapper\\msg.xml");
saxParser.parse(file,new XmlHandler());
}
}
封装逻辑:
总结:
- 可以看到,sax定义了startDocument,endDocument,startElement,endElement,characters这些事件,
- 针对上面的例子来说,startDocument,endDocument只会调用一次,因为只有一个文档嘛,自然就只有一次文档的开始与文档的结束。
- startElement,endElement,characters事件都会调用多次,其中characters用于获取元素内的文本内容。
SAX的优点:
- 解析速度快
- 占用内存少
SAX的缺点:
- 无法知道当前解析标签(节点)的上层标签,及其嵌套结构,仅仅知道当前解析的标签的名字和属性,要知道其他信息需要程序猿自己编码
- 只能读取XML,无法修改XML
- 无法随机访问某个标签(节点)
SAX解析适用场合
- 对于CPU资源宝贵的设备,如Android等移动设备
- 对于只需从xml读取信息而无需修改xml