Android之pull生成XML及XmlSerializer详解

    xiaoxiao2021-04-13  35

    Android之pull生成XML及XmlSerializer详解

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

    知识点

    XmlSerializer实例的源码解析;XmlSerializer类方法详解;pull生成XML的实例;新名词记录{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。

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

    转载请注明原文地址: https://ju.6miu.com/read-669489.html

    最新回复(0)