Android之SAX生成XML
文章链接:http://blog.csdn.net/qq_16628781/article/details/70174654
知识点:
- SAXTransformerFactory类及其对象说明;
- TransformerHandler类及其对象说明;
- Transformer类及其对象说明;
- SAX生成XML实例讲解;
- 新名词记录{SAXTransformerFactory:sax转换工厂类;TransformerHandler:转换事件处理类,触发节点事件;Transformer:设置输出XML文档的属性等;AttributesImpl:设置节点属性的类;}
概述
上一篇讲解了如何利用pull生成XML文件。Android之pull生成XML及XmlSerializer详解
下面我们来讲一下如何利用SAX方式生成XML文件。
SAXTransformerFactory类及其对象
SAXTransformerFactory类继承自TransformerFactory,利用此类,可以创建Transformer类和Templates类的对象(在javax.xml.transform包下面)。
关于SAXTransformerFactory类对象的获取,使用的是下面代码:
SAXTransformerFactory saxTransformerFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
去到静态方法newInstance()可以看到如下:
public static TransformerFactory newInstance()
throws TransformerFactoryConfigurationError {
String className = "org.apache.xalan.processor.TransformerFactoryImpl";
try {
return (TransformerFactory) Class.forName(className).newInstance();
} catch (Exception e) {
throw new NoClassDefFoundError(className);
}
}
因为SAXTransformerFactory的构造方法是受保护的,所以不能够直接new出一个对象。这里是利用反射获取到SAXTransformerFactory类实例,主要是获取到TransformerFactoryImpl类对象。关于TransformerFactoryImpl类,它是由Apache提供的类,放在org.apache.xalan.processor目录下面。关于找不到该类的异常就不管了,系统都找不到,我们还能怎么相信Google。
TransformerHandler类及其对象
获取到SAXTransformerFactory工厂类对象之后,就可以获取转换处理类TransformerHandler的对象了。代码如下:
TransformerHandler transformerHandler = saxTransformerFactory.newTransformerHandler();
那么TransformerHandler类是用来干嘛的呢?TransformerHandler类是用来监听ContentHandler转换事件和转换数据到Result类里面去(对应的设置是setResult(result)方法)。
在利用SAX进行解析XML的时候,我们自定义了一个类,然后继承DefaultHandler处理类,在自定义的类里面,需要重写startElement()、characters()和endElement()等方法。DefaultHandler在继承关系上面,我们也可以看到它是继承了EntityResolver, DTDHandler, ContentHandler, ErrorHandler。看到其中有ContentHandler类。
那么ContentHandler类的作用是什么呢?简单来说,SAX解析基于事件驱动,那么此类的作用就是用户传递和处理事件。我们知道XML文档中的Element是必须要成对出现的,那么这就有两个事件需要触发,开始节点和结束节点。如果解析的时候,parse(InputStream is, DefaultHandler dh)方法参数2需要传入DefaultHandler对象,所以在解析的时候,节点处理类dh会解析到那个节点,就出发哪一个方法。具体请查看
但是这里我们需要的是手动触发事件。所以就需要获取TransformerHandler类对象,此对象可以手动触发事件。在解析使用到的DefaultHandler类和TransformerHandler类具有同样的父类,所以都是可以触发事件的。
获取转换类Transformer
Transformer类其实是用来处理XML文档,并且将它写入到特定的容器中,有以下功能:
设置文档编码方式OutputKeys.ENCODING,
OutputKeys.VERSION版本
是否写出XML文档的说明OutputKeys.OMIT_XML _DECLARATION(取值:yes|no)
文档是否独立STANDALONE(取值:yes|no):no 表示这个 XML 文档不是独立的而是依赖于外部所定义的一个 DTD,yes 表示这个 XML 文档是自包含的(self-contained)。INDENT设置缩进,yes|no。
设置输出
上面说到了,我们如何转换,但是转换的结果应该要怎么处理呢?TransformerHandler类给我们提供了StreamResult()方法,设置输出流。
代码如下:
public void setResult(Result result)
StreamResult实现了Result接口。Result类的作用是建立一个转换结果树。我们要使用的就是其子类。
其构造方法有5个:
public StreamResult()
public StreamResult(OutputStream outputStream)
public StreamResult(Writer writer)
public StreamResult(String systemId)
public StreamResult(File f)
传入的参数是结果的接收对象。
无参构造方法,可以调用set方法设置输出接收对象。OutputStream是输出流,比如用文件打开一个输出流,将转换后的数据写入到文件在,因为输出流不能够在程序中得到输出的内容,所以可以使用Writer,创建一个Result,然后在,利用getBuffer()方法或者totring()方法得到结果,再返回出去。或者是一个File文件参数,先写入本地,然后需要的时候再拿出来。
开始创建XML内容了
前面所做的都是准备工作,准备文件的说明和输出等,下面就是开始真正的进行建立文档内容了。一个节点有开始,那么必须有结束,相同节点是成对出现的。如果不是成对出现,那么就会报不匹配的错误。
startDocument()/endDocument()
开始和结束文档节点要触发的方法。所有节点都需要在这两个方法之间完成。
transformerHandler.startDocument();
transformerHandler.endDocument();
startElement()/endElement()
public void startElement (String uri, String localName,
String qName, Attributes atts)
public void endElement (String uri, String localName,
String qName)
参数解释:
参数1:命名空间的URI。
参数2:节点名称。
参数3:节点的全限定名。
参数Attributes:此节点的属性。例如< user>节点有id属性,那么就需要设置。
设置属性的方法利用的是Attributes接口的实现类AttributesImpl。AttributesImpl类提供了一系列的方法,比如:
//得到属性的数量
public int getLength ()
//获取命名空间
public String getURI (int index)
//得到本地属性名
public String getLocalName (int index)
//获取属性全限定名
public String getQName (int index)
//获取属性类型
public String getType (int index)
//获取第几个属性的值
public String getValue (int index)
//根据命名空间和本地属性名获取位置
public int getIndex (String uri, String localName)
//清除该节点所有属性
public void clear ()
//根据下标移除属性
public void removeAttribute (int index)
//设置节点的URI
public void setURI (int index, String uri)
//设置属性名
public void setLocalName (int index, String localName)
//设置属性全限定名
public void setQName (int index, String qName)
//设置属性类型
public void setType (int index, String type)
//设置属性的值
public void setValue (int index, String value)
其中比较要关注的是:
//新加一个属性
addAttribute (String uri, String localName, String qName,
String type, String value)
//设置属性
public void setAttribute (int index, String uri, String localName,
String qName, String type, String value)
举例说明:
比如我要在节点user里面加入一个id的属性,最终是这样的效果< user id=”1”>
代码实例:
AttributesImpl attributes = new AttributesImpl();
//需要先清除属性,以免上一个节点属性影响本节点属性
attributes.clear();
attributes.addAttribute("", "id", "id", "int",
//最后一个参数设置属性对象
String.valueOf(userBean.getId()));
transformerHandler.startElement("", "user", "user", attributes);
characters()
上面讲完了如何创建节点和给节点加入属性。如果有一个节点下面不再包含子节点,而是一个节点值了。那么该如何来设置值呢?
调用的是public void characters (char ch[], int start, int length);方法
参数1:char字符数组;参数2:数组开始的下边;参数3:开始的长度;
这个方法比较简单哈,只要拿到对应实体的属性值,然后直接设置进去就OK了。
最后我们来看一下中的代码:
对应的实体类UserBean.java
public class UserBean implements Serializable {
//串行化版本统一标识符
private static final long serialVersionUID = 1L;
private int id;
private String userName;
private String password;
private int age;
//省略setter和getter方法
}
buildXmlBySax()方法:
/**
* SAX生成XML
*
* @param userBeanList 实体类集合
* @param outputStream 输出流
*/
public static void buildXmlBySax(List<UserBean> userBeanList, OutputStream outputStream) {
try {
SAXTransformerFactory saxTransformerFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
TransformerHandler transformerHandler = saxTransformerFactory.newTransformerHandler();
Transformer transformer = transformerHandler.getTransformer();
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
Result result = new StreamResult(outputStream);
transformerHandler.setResult(result);
transformerHandler.startDocument();
AttributesImpl attributes = new AttributesImpl();
transformerHandler.startElement("", "users", "users", attributes);
for (UserBean userBean : userBeanList) {
attributes.clear();
attributes.addAttribute("", "id", "id", "", String.valueOf(userBean.getId()));
transformerHandler.startElement("", "user", "user", attributes);
attributes.clear();
transformerHandler.startElement("", "userName", "userName", attributes);
transformerHandler.characters(userBean.getUserName().toCharArray(), 0, userBean.getUserName().length());
transformerHandler.endElement("", "userName", "userName");
transformerHandler.startElement("", "password", "password", attributes);
transformerHandler.characters(userBean.getPassword().toCharArray(), 0, userBean.getPassword().length());
transformerHandler.endElement("", "password", "password");
transformerHandler.startElement("", "age", "age", attributes);
transformerHandler.characters(String.valueOf(userBean.getAge()).toCharArray(), 0, String.valueOf(userBean.getAge()).length());
transformerHandler.endElement("", "age", "age");
transformerHandler.endElement("", "user", "user");
}
transformerHandler.endElement("", "users", "users");
transformerHandler.endDocument();
} catch (TransformerConfigurationException | SAXException e) {
e.printStackTrace();
}
}
最后在activity里面调用此方法,这里我获取了设备内部公共存储空间的,并且建立一个文件buildusersbysax.xml,文件名最好小写,别问我为什么,因为可以跨平台。
代码如下:
//文件夹
File file = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
//读取本地XML文件
InputStream inputStream = getResources().openRawResource(R.raw.users);
//解析本地XML文件
List<UserBean> userBeanList = XmlUtil.parseXmlByPull(inputStream);
//最后在根据实体集合,生成XML文件
try {
String outPutPath = file.getAbsolutePath() + "buildusersbysax.xml";
File outFile = new File(outPutPath);
if (outFile.exists()){
outFile.delete();
outFile.createNewFile();
}else {
outFile.createNewFile();
}
CommonLog.logInfo("path:" + outFile.getAbsolutePath());
FileOutputStream fileOutputStream = new FileOutputStream(outFile);
XmlUtil.buildXmlBySax(userBeanList, fileOutputStream);
} catch (IOException e) {
e.printStackTrace();
}
下面是运行生成的XML文件。截图
总结
主要讲解了如何获取SAXTransformerFactory工厂类,建立TransformerHandler转换的处理类,然后在获取Transformer转换类,以及每个类的作用和联系。最后详细说明了下如何触发生成文档需要的每个节点,包括开始建立文档(包括建立XML文件的说明),开始建立节点并且设置节点属性和设置节点值。最后千万别忘了,每个节点都是要成对出现,并且都需要关闭。
关于如何确保关闭节点,我们可以start一个节点,立马就写一个关闭的方法,然后在两个中间写我们需要的代码。
总的来说,sax加你文档并不难。如果有更加复杂的结构,只要理清了结构,那就都不是问题了。
以上就是所有内容,如有任何问题,请及时与我联系,谢谢!