Android之SAX生成XML及TransformerHandler解析

Android之SAX生成XML

文章链接:http://blog.csdn.net/qq_16628781/article/details/70174654

知识点:

  1. SAXTransformerFactory类及其对象说明;
  2. TransformerHandler类及其对象说明;
  3. Transformer类及其对象说明;
  4. SAX生成XML实例讲解;
  5. 新名词记录{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加你文档并不难。如果有更加复杂的结构,只要理清了结构,那就都不是问题了。

以上就是所有内容,如有任何问题,请及时与我联系,谢谢!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值