前端之家收集整理的这篇文章主要介绍了
SAX解析和生成XML文档,
前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
一、前言
SAX操作xml是基于事件来完成的,自己只负责调用解析的方法,然后具体解析操作都是交给DefaultHandler处理者来完成的,总的来说使用SAX解析和生成xml文档还是比较方便的 。
二、准备条件
因为SAX是jdk自带的解析方式,所以不用添加jar包引用。
三、使用SAX实战
1、解析xml文档
实现思路:
<1>先由SAXParserFactory这个工厂的实例生产一个SAXParser解析器;
<2>然后根据读取的xml路径,传递给SAXParser这个解析器,再调用parse()方法;
<3>在parse()方法中需要传递DefaultHandler这个类的扩展类的实例,因为它才会真正去一步步去解析xml文档的;
<4>在DefaultHandler扩展类中需要重写startDocument(),endDocument()等等方法,因为他们方法内部有返回的具体文档的结果。
具体代码如下:
- importjava.io.BufferedOutputStream;
- importjava.io.File;
- importjava.io.FileInputStream;
- importjava.io.FileNotFoundException;
- importjava.io.FileOutputStream;
- importjava.io.IOException;
- importjava.io.InputStream;
- importjava.util.ArrayList;
- importjava.util.List;
-
- importjavax.xml.parsers.ParserConfigurationException;
- importjavax.xml.parsers.SAXParser;
- importjavax.xml.parsers.SAXParserFactory;
- importjavax.xml.transform.OutputKeys;
- importjavax.xml.transform.Result;
- importjavax.xml.transform.Transformer;
- importjavax.xml.transform.TransformerConfigurationException;
- importjavax.xml.transform.sax.SAXTransformerFactory;
- importjavax.xml.transform.sax.TransformerHandler;
- importjavax.xml.transform.stream.StreamResult;
-
- importorg.xml.sax.Attributes;
- importorg.xml.sax.InputSource;
- importorg.xml.sax.SAXException;
- importorg.xml.sax.helpers.AttributesImpl;
- importorg.xml.sax.helpers.DefaultHandler;
-
-
-
-
-
- publicclassSAXOperateXmlDemo{
- publicvoidparseXml01(){
- StringxmlPath="D:\\project\\dynamicWeb\\src\\resource\\user01.xml";
- StringxmlName=xmlPath.substring(xmlPath.lastIndexOf("\\"));
- try{
-
- SAXParserFactorysaxParserFactory=SAXParserFactory.newInstance();
-
- SAXParsersaxParser=saxParserFactory.newSAXParser();
-
-
-
-
-
-
- InputStreaminputStream=newFileInputStream(newFile(xmlPath));
-
-
-
- saxParser.parse(inputStream,newXmlSAXHandler01());
- }catch(ParserConfigurationExceptione){
- e.printStackTrace();
- }catch(SAXExceptione){
- }catch(FileNotFoundExceptione){
- }catch(IOExceptione){
- }
- }
- publicstaticvoidmain(String[]args){
- SAXOperateXmlDemodemo=newSAXOperateXmlDemo();
- demo.parseXml01();
-
-
-
-
-
- classXmlSAXHandler01extendsDefaultHandler{
- @Override
- publicvoidstartDocument()throwsSAXException{
- System.out.println("---->startDocument()isinvoked...");
- super.startDocument();
- publicvoidendDocument()throwsSAXException{
- System.out.println("---->endDocument()isinvoked...");
- super.endDocument();
- publicvoidstartElement(Stringuri,StringlocalName,StringqName,Attributesattributes)throwsSAXException{
- System.out.println("-------->startElement()isinvoked...");
- super.startElement(uri,localName,qName,attributes);
- publicvoidendElement(Stringuri,StringqName)throwsSAXException{
- System.out.println("-------->endElement()isinvoked...");
- super.endElement(uri,qName);
- publicvoidcharacters(char[]ch,intstart,intlength)throwsSAXException{
- System.out.println("------------>characters()isinvoked...");
- super.characters(ch,start,length);
- }
上面代码简单解析了一个xml,user01.xml文件的内容如下:
<?xmlversion="1.0"encoding="utf-8"?>
- <Root>Content</Root>
接下来执行该类的main方法,console效果如下:
根据控制台的显示可知:
<1>解析类必须继承DefaultHandler这个类,重写自己需要获取节点信息的方法,不重写的情况下会调用父类的对应方法,所以不影响程序;
<2>XmlSAXHandler01这个处理者来完成xml的解析工作,并且调用方式是按照xml层级关系来处理的,比如最开始调用startDocument()获取Document对象,然后再递归调用startElement()获取根节点以及子节点的信息,其中的characters()用于获取节点文本内容信息,待节点解析完毕之后会调用endElement(),同样整个xml解析完毕之后会调用endDocument()结束。
上面只是简单的获取了xml的根目录的元素,接下来使用DefaultHandler这个处理者怎么获取节点内的信息。
具体代码如下:
publicvoidparseXml02(){
StringxmlPath="D:\\project\\dynamicWeb\\src\\resource\\user02.xml";
newXmlSAXHandler02());
}
对应的XMLSAXHandler02的代码如下:
上面的xml在src下面,user02.xml具体如下:
接下来执行该类的main方法,console效果如下:
根据控制台的显示可知:
<1>XMLSAXHandler02在解析的时候执行方法是从最外层往内、从上往下依次解析的,并且每一次解析节点都是startElement()和endElement()成对出现的;
<2>图中显示了每一个节点解析的信息,并且startElement()和endElement()的区别在于前面方法有携带属性,而后面方法没有;
<3>图中之所以出现三个“节点元素文本内容”是XMLSAXHandler02也是把非标签的文本当前一个节点了,所以在解析的时候要排除这种情况,以免影响最终想要的结果。
另外发现:
<1>查看父类的方法发现它们的方法体什么都没做;
<2>SAX不支持重新修改XML的结构;
<3>如果正常业务需求,解析xml之后不可能只是简单输出下内容,而是要返回成一个集合或者其他形式返回,目前情况可以使用全局的ArrayList集合来完成解析之后节点内容的封装。
接下来需要实现如何封装SAX解析完毕的XML文档,都知道java是面向对象编程的,那么这个时候可以把文档中的每一个节点都看成一个对象,包括节点里面的属性也是一样,那么在解析XML的时候直接使用javabean封装一下,思路就非常清晰了,但是现在还有还一个问题: 如何在SAXParser调用parse()方法之后返回最终的结果集呢?就目前肯定不行的,其一方法没有返回值,其二解析操作完全交给DefaultHandler去做了,所以这种情况下肯定不能使用普通变量或者全局变量,因为使用了之后会随着当前操作类的实例化生命周期而存在,并且DefaultHandler在调用的时候又需要产生一个新的实例,这样前后就没有关联性了。 所以只能使用静态ArrayList来完成了。
具体操作如下:
1、前面说了构建节点对象和属性对象,具体代码如下:
publicclassNode{
privateLongid;
privateStringname;
privateStringtext;
privateList<Attribute>attributeList;
privateList<Node>nodeList;
publicLonggetId(){
returnid;
publicvoidsetId(Longid){
this.id=id;
publicStringgetName(){
returnname;
publicvoidsetName(Stringname){
this.name=name;
publicStringgetText(){
returntext;
publicvoidsetText(Stringtext){
this.text=text;
publicList<Attribute>getAttributeList(){
returnattributeList;
publicvoidsetAttributeList(List<Attribute>attributeList){
this.attributeList=attributeList;
publicList<Node>getNodeList(){
returnnodeList;
publicvoidsetNodeList(List<Node>nodeList){
this.nodeList=nodeList;
}
publicclassAttribute{
privateStringname;
privateStringvalue;
publicStringgetName(){
returnname;
publicvoidsetName(Stringname){
this.name=name;
publicStringgetValue(){
returnvalue;
publicvoidsetValue(Stringvalue){
this.value=value;
}
2、在SAXOperateXmlDemo这个操作类中添加两个常量,具体如下:
publicstaticList<Node>nodeList=null;
publicstaticNodenode=null;
不光集合需要全局静态化,节点对象也要全局静态化,因为解析过程中获取标签名称和标签内文本是分开操作的,如果不这样对象的属性值无法完整获取。
3、定义xml文档解析方法,具体如下:
4、对应的解析处理者代码如下:
classXmlSAXHandler03extendsDefaultHandler{
SAXOperateXmlDemo.nodeList=newArrayList<Node>();
SAXOperateXmlDemo.node=newNode();
SAXOperateXmlDemo.node.setId(null);
SAXOperateXmlDemo.node.setName(qName);
List<Attribute>attributeList=newArrayList<Attribute>();
if(attributes.getLength()>0){
for(inti=0;i<attributes.getLength();i++){
Attributeattribute=newAttribute();
attribute.setName(attributes.getQName(i));
attribute.setValue(attributes.getValue(i));
attributeList.add(attribute);
SAXOperateXmlDemo.node.setAttributeList(attributeList);
if(SAXOperateXmlDemo.node!=null){
SAXOperateXmlDemo.node.setText(newString(ch,226); color:inherit; line-height:18px">
SAXOperateXmlDemo.nodeList.add(SAXOperateXmlDemo.node);
SAXOperateXmlDemo.node=null;
}
5、代码中解析的user03.xml的结构如下:
<userid="001">UserInfo_1</user>
<userid="002">UserInfo_2</user>
</Root>
接下来执行该解析xml的方法,控制台显示效果如下:
根据控制台的显示可知:
<1>使用全局静态变量完成完成了对Xml解析之后的封装工作,并且在输出的时候没有问题,但是需要注意的是去掉空文本节点这种特殊情况,否则会出现获取的节点内的内容为"\n\t" 等等结果;
<2>虽然功能是完成了,但是如果Xml文档中录入的不是文本,而是添加的详细的子节点呢?这样每一个节点就是一个Node对象,在查询和使用的时候非常的不方便。
所以为了避免这种情况,作出如下改动:
因为需求只需要获取User信息,那么不用每一个解析的节点都封装成一个对象,并且属性对象和节点对象可以合并,不用分太开这样不易于后期维护。
具体操作如下:
假设现在需要解析的xml文档如下:
<Users>
<userid="9527">
<name>admin</name>
<age>40</age>
<hobby>managesomeone!</hobby>
</user>
<userid="9632">
<name>chenghui</name>
<age>110</age>
<hobby>codesomething!</hobby>
</user>
</Users>
然后创建User实体类封装,具体如下:
publicclassUser{
privateLongage;
privateStringhobby;
publicLonggetId(){
returnid;
publicvoidsetId(Longid){
this.id=id;
publicLonggetAge(){
returnage;
publicvoidsetAge(Longage){
this.age=age;
publicStringgetHobby(){
returnhobby;
publicvoidsetHobby(Stringhobby){
this.hobby=hobby;
}
解析当前xml的方法具体如下:
publicvoidparseXml04(){
StringxmlPath="D:\\project\\dynamicWeb\\src\\resource\\user04.xml";
newXmlSAXHandler04());
if(SAXOperateXmlDemo.userList.size()>0){
for(Useruser:SAXOperateXmlDemo.userList){
System.out.println("【Id】"+user.getId());
System.out.println("【姓名】"+user.getName());
System.out.println("【年龄】"+user.getAge());
System.out.println("【爱好】"+user.getHobby());
}
并且当前解析类需要添加两个全局静态变量,具体如下:
publicstaticList<User>userList=null;
publicstaticUseruser=null;
对应的解析xml处理者代码如下:
classXmlSAXHandler04extendsDefaultHandler{
privateStringcurrentQName;
SAXOperateXmlDemo.userList=newArrayList<User>();
if(qName.equals("user")){
SAXOperateXmlDemo.user=newUser();
SAXOperateXmlDemo.user.setId(Long.valueOf(attributes.getValue("id")));
this.currentQName=qName;
if(qName.equals("user")){
SAXOperateXmlDemo.userList.add(SAXOperateXmlDemo.user);
SAXOperateXmlDemo.user=null;
this.currentQName=null;
Stringcontent=newString(ch,length);
if(SAXOperateXmlDemo.user!=null&¤tQName!=null){
if(currentQName.equals("name")){
SAXOperateXmlDemo.user.setName(content);
}elseif(currentQName.equals("age")){
SAXOperateXmlDemo.user.setAge(Long.valueOf(content));
}elseif(currentQName.equals("hobby")){
SAXOperateXmlDemo.user.setHobby(content);
}
直接运行该解析方法,控制台显示效果如下:
好了,现在满足需求了 解析自己需要的节点然后封装成集合展示出来。
2、生成xml文档
SAX能够解析xml,同样肯定能生成xml,而且使用起来也不是很复杂。
实现思路:
<1>创建保存xml的结果流对象StreamResult;
<2>然后利用SAXTransformerFactory这个工厂创建TransformerHandler这个操作者;
<3>操作这个TransformerHandler获取Transformer,利用Transformer创建节点信息;
具体代码如下:
执行该生成方法,控制台显示效果如下:
然后看看生成的XML,结构如下:
结果显示达到了期望,但是有一个问题:
如果使用transformer.setOutputProperty(OutputKeys.ENCODING,"UTF-8");重新指定了编码,插入的中文会变成乱码,现在没有想到解决方案。。
但是如果不指定编码 却没有问题,显示结果是上图中的默认的UTF-8。
原文转载自:http://blog.csdn.net/chenghui0317/article/details/11990891