概述:之前做的网络相关的应用,里面涉及到了xml的解析,由于急于完成任务也没有设计就直接敲代码。这几天给一个朋友做项目的时候也涉及到了xml的解析,而且解析的内容比较多,我查看了以前的项目中的相关代码,顿时觉得很多代码都是可以优化的。在此写两个通用的xml解析方法,当然这里所讲的通用只是在一定程度上并且需要遵守一些规范。
1、为什么需要写一个通用xml解析方法。
当需要解析不同的xml节点。你有可能是在xml解析的时候匹配不同节点并且节点名都是写死的,这样的话你解析不同的节点就需要不同的解析方法。当然这种方式是最简单也是最笨的方法。为了减少代码把代码写得更有质量那么你就需要考虑设计一个通用的xml解析方法。
2、解析思路。
一般情况下,xml的解析结果最好放在一个实体类对象中,那样的话你使用起来非常方便(当然也更OO了),你也可以选择其他的方法把解析结果保存下来,不过个人觉得这种方式是比较好的。在解析过程中你需要做的是什么呢?这是解析的关键。其实就是把要解析的结果设置给对象的属性(成员变量),考虑到这点,那么肯定是需要知道对象有哪些属性啊,那就给实体类加一个方法(其实这里是做一定的规范)用于获得属性。知道了属性名以后下一步当然就是设置这些属性的值。因为不同的实体类的属性不同,所以设置值采用反射机制。大体上的思路就是这个样子。具体代码后面讲。
3、解析xml的格式类型。
文字只写两种xml格式的解析。其他格式你可以参考本文的思路任意发挥。
①只有节点中内容:如
<?xml version="1.0" encoding="UTF-8" ?> <Result> <StuId>30323</StuId> <ClassID>10042</ClassID> </Result>
②只有节点属性:如
<?xml version="1.0" encoding="UTF-8" ?> <Result> <ProjLst Name="测试1" Id="1" /> <ProjLst Name="测试2" Id="2" /> <ProjLst Name="测试3" Id="3" /> </Result>
4、如何实现。
①根据设计思路,你需要一个实体类,但是实体类有一定的规范(为了解析)。所以这些规范还需要实现一些统一的方法,于是就有了一个抽象类:BaseObj。
BaseObj
/*********************************************************** *@description : This class function is TODO * * @create author : kwzhang * @create date :2013-2-28 * @modify author : * @modify date : * @contact: vanezkw@163.com * **********************************************************/ package com.vane.elearning.model; import java.lang.reflect.Field; /** * @author kwzhang * */ public abstract class BaseObj { public abstract String[] getNodes(); public void setParamater(String tag, Object value) { try { Field field = getClass().getField(tag); field.setAccessible(true); field.set(this, value); } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } }
②根据具体的节点类型生成类的成员。这里先看看需要解析的xml。
<?xml version="1.0" encoding="UTF-8" ?> <DsXml> <IsLog>true</IsLog> <GradeID>10001</GradeID> <GradeName>高一年级</GradeName> <ClassID>10010</ClassID> <ClassName>高一(01)班</ClassName> <UserID>10000</UserID> <UserName>张三</UserName> </DsXml>
③对应的实体类。
View Code
/*********************************************************** *@description : This class function is TODO * * @create author : kwzhang * @create date :2013-2-28 * @modify author : * @modify date : * @contact: vanezkw@163.com * **********************************************************/ package com.vane.elearning.model; import java.io.Serializable; /** * @author kwzhang * */ public class Student extends BaseObj implements Serializable { private static final long serialVersionUID = 1L; public String GradeID, GradeName, ClassID, ClassName, UserID, UserName; public Student() { } @Override public String[] getNodes() { return new String[] { "GradeID", "GradeName", "ClassID", "ClassName", "UserID", "UserName" }; } }
实体类中的是这样规范的:getNodes()返回的是xml的节点名,命名必须相同,并且成员变量名必须和节点名相同。当然这里实现Serializable 接口只是我自己的项目中的需求而已,和本文无关。
④最关键是如何解析。
View Code
/** * @description :解析节点中的内容,封装成对象模型。 * @author : kwzhang * @create :2013-2-28 * @param in * @param obj * @throws Exception * @return :void */ public static <T extends BaseObj> void streamText2Model(InputStream in, T obj) throws Exception { pullParser.setInput(in, encode); int eventType = pullParser.getEventType(); String[] nodes = obj.getNodes(); String nodeName = null; boolean success = true; while (eventType != XmlPullParser.END_DOCUMENT && success) { switch (eventType) { case XmlPullParser.START_DOCUMENT: break; case XmlPullParser.START_TAG: nodeName = pullParser.getName(); break; case XmlPullParser.TEXT: if ("IsLog".equals(nodeName) && pullParser.getText().equals("false")) { success = false; break; } for (int i = 0; i < nodes.length; i++) { if (nodes[i].equals(nodeName)) { obj.setParamater(nodeName, pullParser.getText()); } } break; case XmlPullParser.END_TAG: break; } eventType = pullParser.next(); } }
当然里面的一些变量在类初始化的时候就完成了。如下:
private static String encode = "utf-8"; public static XmlPullParser pullParser; static { try { pullParser = XmlPullParserFactory.newInstance().newPullParser(); } catch (XmlPullParserException e) { e.printStackTrace(); } }
⑤如何使用.如下:
XmlUtils.streamText2Model(result, ActMain.student);
很简单吧。result就是xml的数据流。具体的细节可以自己体会一下。这个解析类在一定程度上可以通用,也就是你的xml格式符合“只有节点中内容”那么就可以这么通用。为了方便下文做说明暂且把这种类型的xml称为“类型A”。
⑥说说另一种格式“只有节点属性”如何“通用”解析。为了方便下文做说明暂且把这种类型的xml称为“类型B”。下文所讲的都是针对类型B的相关代码。类型B的xml如下:
View Code
<?xml version="1.0" encoding="UTF-8" ?> <DsXml> <ProjLst KeliName="测试1" KeliId="170" SubId="13" ExeTp="1" ExeType="预习" ExeDt="2013-2-27" ExeCount="4" SubName="信息技术" /> <ProjLst KeliName="测试2" KeliId="154" SubId="13" ExeTp="1" ExeType="预习" ExeDt="2012-11-19" ExeCount="2" SubName="信息技术" /> <ProjLst KeliName="测试2" KeliId="150" SubId="13" ExeTp="3" ExeType="课后" ExeDt="2012-11-15" ExeCount="2" SubName="信息技术" /> </DsXml>
⑦类型B实体类如下:(其实和类型A是一样的)
/*********************************************************** *@description : This class function is TODO * * @create author : kwzhang * @create date :2013-2-28 * @modify author : * @modify date : * @contact: vanezkw@163.com * **********************************************************/ package com.vane.elearning.model; import java.io.Serializable; /** * @author kwzhang * */ public class Keli extends BaseObj implements Serializable { private static final long serialVersionUID = 1L; public String KeliName, KeliId, SubId, ExeTp, ExeType, ExeDt, ExeCount, SubName; public Keli() { } @Override public String[] getNodes() { return new String[] { "KeliName", "KeliId", "SubId", "ExeTp", "ExeType", "ExeDt", "ExeCount", "SubName" }; } }
⑧类型B解析方法如下:
View Code
/** * @description : 解析xml中的属性,封装成对象模型。 * @author : kwzhang * @create :2013-2-28 * @param in * @param obj * @throws Exception * @return :void */ public static <T extends BaseObj> List<T> streamParam2Model(InputStream in, T obj) throws Exception { pullParser.setInput(in, encode); int eventType = pullParser.getEventType(); ArrayList<T> list = new ArrayList<T>(4); String[] nodes = obj.getNodes(); while (eventType != XmlPullParser.END_DOCUMENT) { switch (eventType) { case XmlPullParser.START_DOCUMENT: break; case XmlPullParser.START_TAG: String name = pullParser.getName(); boolean flag = false; if (null == name || name.equals("")) { break; } for (int i = 0; i < nodes.length; i++) { String value = pullParser.getAttributeValue(null, nodes[i]); flag |= (null != value); obj.setParamater(nodes[i], value); } if (flag) { list.add(obj); } Constructor<T> constructor = (Constructor<T>) obj.getClass().getConstructor(); obj = constructor.newInstance(); break; case XmlPullParser.END_TAG: break; } eventType = pullParser.next(); } return list; }
⑨如何使用类别B的解析。
ArrayList<TmInfo> datas = (ArrayList<TmInfo>) XmlUtils.stream2Tm(result, new TmInfo());
总结:虽然这里只是写了这两种类型,但是可以根据这种反射机制的思路完成更复杂的xml解析。使用的时候一定要注意变量命名的规范。用这样的方式进行解析的话代码设计更优雅,而且xml解析的时候会根据你的变量去解析,而不是写死。
PS:转载请注明出处。由于能力有限,有不妥的地方请读者留下宝贵的建议。欢迎交流,期待共同进步,Android交流群:196761677。