Android之pull生成XML及XmlSerializer详解

Android之pull生成XML及XmlSerializer详解

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

知识点

  1. XmlSerializer实例的源码解析;
  2. XmlSerializer类方法详解;
  3. pull生成XML的实例;
  4. 新名词记录{XmlSerializer;XmlSerializerFactory;StringWriter}

概述

前面的文章讲了3中常用的解析XML的方式,详情请看3种解析XML的方法

下面我们就要来看下,如何生成XML文档,以便在内存中的对象进行序列化保存起来,那么就可以进行数据保存和共享了。

XmlSerializer实例

获取XML序列化XmlSerializer类实例:

XmlSerializer xmlSerializer = Xml.newSerializer();

//或者
try {
            //还可以这样获取序列化实例
            XmlPullParserFactory xmlPullParserFactory = XmlPullParserFactory.newInstance();
            XmlSerializer xmlSerializer = xmlPullParserFactory.newSerializer();
        } catch (XmlPullParserException e) {
            e.printStackTrace();
        }

首先我们查看到newSerializer()方法实现,如下:

此方法在Xml.java类下面。

public static XmlSerializer newSerializer() {
        try {
            return XmlSerializerFactory.instance.newSerializer();
        } catch (XmlPullParserException e) {
            throw new AssertionError(e);
        }
    }

由上面可以看到,系统建立一个内部静态类,静态类是用了一个工厂模式来管理XmlSerializer实例,代码如下:

代码在Xml.java文件下面。

static class XmlSerializerFactory {
        static final String TYPE
                = "org.kxml2.io.KXmlParser,org.kxml2.io.KXmlSerializer";
        static final XmlPullParserFactory instance;
        static {
            try {
                instance = XmlPullParserFactory.newInstance(TYPE, null);
            } catch (XmlPullParserException e) {
                throw new AssertionError(e);
            }
        }
    }

我们知道,静态对象/静态代码块一开始就会被JVM加载到内存。可以看到上面的instance实例,是一个静态的final对象,最开始的时候就被实例化了。然后我们看到TYPE,这分明是KXmlSerializer类的全限定名,这是不是要放大招了–使用反射创建实例?

为了一探究竟,我们到XmlPullParserFactory.newInstance(TYPE, null)方法实现里面瞧一瞧。

很奇怪的是,传入的两个参数,居然都没有使用,Google的解释是,不允许此工厂随意的创建paser和serializer。

跟踪着源码看到下面的代码:
此代码在XmlPullParserFactory.java类里面

protected XmlPullParserFactory() {
        parserClasses = new ArrayList<String>();
        serializerClasses = new ArrayList<String>();

        try {
            parserClasses.add(Class.forName("org.kxml2.io.KXmlParser"));
            serializerClasses.add(Class.forName("org.kxml2.io.KXmlSerializer"));
        } catch (ClassNotFoundException e) {
            throw new AssertionError();
        }
    }

来到这里,终于真相了。原来Google真的使用了反射来获取解析XML和生成XML的两个类,KXmlParser和KXmlSerializer。可以看到,这里使用两个数组将这两个类装载起来,需要的时候可以getParserInstance()和getSerializerInstance()分别获得解析XML实例和序列化XML实例。

但是很奇怪的是,在获取XmlPullParser实例的时候,可以使用使用反射获取到的解析XML实例,也可以使用Xml类的静态方法:newPullParser()方法获取到XmlPullParser实例。代码如下:

此代码在Xml.java文件下。

public static XmlPullParser newPullParser() {
        try {
            KXmlParser parser = new KXmlParser();
            parser.setFeature(XmlPullParser.FEATURE_PROCESS_DOCDECL, true);
            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
            return parser;
        } catch (XmlPullParserException e) {
            throw new AssertionError();
        }
    }

可以看到使用的new了一个KXmlParser对象,却不是利用上面反射得到的对象。更奇怪的是,同样的道理在获取序列化KXmlSerializer时,却不是这样子了,使用Xml.java类的newSerializer()方法和XmlPullParserFactory.java类的newSerializer()方法获得的实例,都是通过反射获得。这里就有点想不明白,为毛Google要这样做?

下面给出获取解析和序列化的全部方式:

try {
            XmlPullParserFactory xmlPullParserFactory = XmlPullParserFactory.newInstance();
           //解析实例
            XmlPullParser xmlPullParser = xmlPullParserFactory.newPullParser();
            XmlPullParser pullParser = newPullParser();

            //序列化实例
            XmlSerializer xmlSerializer =  xmlPullParserFactory.newSerializer();
            XmlSerializer xmlSerializer1 = newSerializer();
        } catch (XmlPullParserException e) {
            e.printStackTrace();
        }

XmlSerializer类

因为XmlSerializer类是一个接口类,而KXmlSerializer是其子类。KXmlSerializer不能new出来,必须要利用反射的方式调用。我们这里看到XmlSerializer类的各个方法作用和使用。

1、void setFeature(String name, boolean state)

设置解析器的行为。例如是否打开命名空间处理功能等等。

参数1:设置feature,可以控制解析器的行为。例如可以设置”http://xml.org/sax/features/namespaces“:打开、关闭名空间处理功能、”http://xml.org/sax/features/validation“:是否打开校验。
当关闭校验的时候可以大大节约内存空间并且大大提高解析速度。因此如果使用的XML文档是可靠的,例如程序生成的,最好关闭校验。

参数2:表明该行为是否打开。

对应获取的方法:
boolean getFeature(String name)

2、 void setProperty(String name,Object value)

设置属性的值。参数name最好是唯一的URI。

对应的根据name获取属性的方法为:Object getProperty(String name);未知的属性名则会返回null。

3、void setOutput (OutputStream os, String encoding)

根据给定的编码方式,对序列化后的内容通过OutputStream进行输出。

注意:此方法必须要在startDocument()之前调用。

4、void setOutput (Writer writer)

序列化后的内容,写入到writer中,然后可以对writer进行操作。唯一的问题就是:这里并没有对序列化过程中,指定编码方式。

注意:此方法必须要在startDocument()之前调用。

5、void startDocument (String encoding, Boolean standalone)

开始文档节点,并写入XML文件的声明,例如,如果有指定encoding=utf-8,那么就在XML文档第一句声明为:

xmlSerializer.setPrefix("yaojt", "http://com.yaojt.banana");

那么在生成的文件中,会在根节点设置命名空间。如下:

<users xmlns:yaojt="http://com.yaojt.banana">

对应的获取命名空间的方法:String getPrefix (String namespace, boolean generatePrefix)。由上面可知,获取到的密码空间就是设置的命名空间。如果没有设置命名空间,那么返回null。

7、int getDepth()

获取当前element的深度。调用startTag()方法,深度加1,反之,调动endTag()方法,深度减1;对应的值:0–在文档外部;1–根布局或者节点;2–foobar(Foobar?播放器,难道是看得见的char?即是节点对应的value);

8、String getNamespace ()

获取当前节点的命名空间,当前节点的命名空间是由startTag()方法的第一个参数决定的。如果startTag(“”, …),那么返回”“;如果startTag(null, …),那么返回null;

9、String getName()

返回当前节点由startTag()参数2决定的name。在调用startTag()之前,返回的都是为null。

10、XmlSerializer startTag (String namespace, String name)

参数1:当前节点命名空间。如果没有prefix,那么系统会自动创建一个prefix,需要设置,则可以在此方法之前调用setPrefix()设置命名空间。如果没有命名空间,那么对应的XML节点下,只会打印节点的名字。如果传入的是”“,那么在XML下会打印成:xmlns=”。如果已经定义的命名空间,那么会抛出不合法状态异常。

参数2:节点的名字。

对应的关闭tag的方法:XmlSerializer endTag (String namespace, String name)

注意:startTag()和endTag()必须成对出现,而且命名空间和节点名称必须完全相同。否则会报错误java.lang.IllegalArgumentException:

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方法

}
/**
     * Pull生成XML
     *
     * @param userBeanList 实体类集合
     * @param outputStream 输出流
     */
    public static String buildXmlByPull(List<UserBean> userBeanList, OutputStream outputStream) {
        XmlSerializer xmlSerializer = newSerializer();
        /*
        try {
            //还可以这样获取序列化实例
            XmlPullParserFactory xmlPullParserFactory = XmlPullParserFactory.newInstance();
            XmlSerializer xmlSerializer = xmlPullParserFactory.newSerializer();
        } catch (XmlPullParserException e) {
            e.printStackTrace();
        }
        */

        String result = null;
        try {
            xmlSerializer.setOutput(outputStream, "utf-8");
            StringWriter stringWriter = new StringWriter();
//            xmlSerializer.setOutput(stringWriter);
            String np = xmlSerializer.getNamespace();

            xmlSerializer.startDocument("utf-8", true);
            xmlSerializer.setPrefix("yaojt", "http://com.yaojt.banana");
            xmlSerializer.startTag(null, "users");
            for (UserBean userBean : userBeanList) {
                xmlSerializer.startTag(null, "user");
                xmlSerializer.attribute(null, "id", String.valueOf(userBean.getId()));

                xmlSerializer.startTag(null, "userName");
                xmlSerializer.text(userBean.getUserName());
                xmlSerializer.endTag(null, "userName");

                xmlSerializer.startTag(null, "password");
                xmlSerializer.text(userBean.getPassword());
                xmlSerializer.endTag(null, "password");

                xmlSerializer.startTag(null, "age");
                xmlSerializer.text(String.valueOf(userBean.getId()));
                xmlSerializer.cdsect("cdsect");
                xmlSerializer.comment("comment");
                xmlSerializer.ignorableWhitespace("ignorable White space");
                xmlSerializer.endTag(null, "age");

                xmlSerializer.endTag(null, "user");
            }
            xmlSerializer.endTag(null, "users");
            xmlSerializer.endDocument();

            xmlSerializer.flush();
            outputStream.close();
            result = stringWriter.toString();
            CommonLog.logInfo("result:" + result);
            return result;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

方法调用

File file = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);

InputStream inputStream = getResources().openRawResource(R.raw.users);
List<UserBean> userBeanList = XmlUtil.parseXmlByPull(inputStream);

try {
            String outPutPath = file.getAbsolutePath() + "allusersbypull.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.buildXmlByPull(userBeanList, fileOutputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }

parseXmlByPull(inputStream) 方法。以上方法对着看就可以了。再次提醒,关于startTag()和endTag()必须要成对出现,否则会报错。

下面是运行的效果截图。
因为我是将文件放在了内部公共目录根目录的DCIM文件夹下(这个是专门放置图片和视频的目录),所以会在前面加上了DCIM前缀。
如图:
图片描述

得到的XML效果图:
图片描述


总结

主要讲解了XmlSerializer实例的获取源码,以及XmlSerializer类的所有方法。以及最后的如何生成XML的实例。

关于实体类序列化成XML文件是很有用的,实体类只能在程序运行时存在内存中,如果需要持久化和分享数据,可以转成XML格式,就可以传输共享了。

下一篇,要讲下使用sax和dom生成XML。

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值