在网络上传输数据时最常用的格式有两种:XML和JSON格式。XML格式的数据扩展性比较好,格式比较统一;JSON数据格式比较简单,体积比较小,在实际的项目中应用比较多。
对于XML解析方式挺多的,比如Pull解析,SAX解析和Dom解析。现在逐一用一个案例说明该如何使用。
假设解析的数据格式如下:
String responeData = "<apps>
<app>
<id>1</id>
<name>Google</name>
<version>1.0</version>
</app>
<app>
<id>2</id>
<name>Chrome</name>
<version>2.1</version>
</app>
<app>
<id>3</id>
<name>WeChat</name>
<version>3.5</version>
</app>
</apps>"
使用Pull的方式进行解析:
try {
XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
//将xml数据设置进去
parser.setInput(new StringReader(responeString));
//获取当前解析事件
int eventType = parser.getEventType();
String id = "";
String name = "";
String version = "";
//eventType != XmlPullParser.END_DOCUMENT 说明解析工作还没完成
while (eventType != XmlPullParser.END_DOCUMENT){
//获取当前节点名字
String nodeName = parser.getName();
switch (eventType){
case XmlPullParser.START_TAG:
//开始解析某个节点
if ("id".equals(nodeName)){
//获取节点内具体的内容
id = parser.nextText();
}else if ("name".equals(nodeName)){
name = parser.nextText();
}else if ("version".equals(nodeName)){
version = parser.nextText();
}
break;
case XmlPullParser.END_TAG:
//完成解析某个节点
if ("app".equals(nodeName)){
Log.d(TAG, "parseXMLWithPull: id = "+id);
Log.d(TAG, "parseXMLWithPull: name = "+name);
Log.d(TAG, "parseXMLWithPull: version = "+version);
}
break;
}
//获取下一个解析事件
eventType = parser.next();
}
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Pull解析的可以主动从解析器中获取事件,正是由于这个元素我们可以在满足某个条件时不再获取事件,从而结束解析,也就是说Pull解析可以不完全解析整个XML文件,可以只解析到我们所需要的地方,这样可以更节省资源,也是Pull解析最大的特点之一。
对于SAX解析,对比Pull解析用法要稍微复杂一点,但是语意方面比较清楚,具体简单用法如下:
先创建一个新类MyHandler继承自DefaultHandler,并重写父类5个方法:
public class MyHandler extends DefaultHandler {
private static final String TAG = "MyHandler";
private String nodeName;
private StringBuilder id;
private StringBuilder name;
private StringBuilder version;
@Override
public void startDocument() throws SAXException {
//开始XML解析的时候调用
super.startDocument();
id = new StringBuilder();
name = new StringBuilder();
version = new StringBuilder();
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
//开始解析某个节点的时候调用
super.startElement(uri, localName, qName, attributes);
//记录当前节点名字
nodeName = localName;
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
//获取节点中内容时调用
super.characters(ch, start, length);
if ("id".equals(nodeName)){
id.append(ch,start,length);
}else if ("name".equals(nodeName)){
name.append(ch,start,length);
}else if ("version".equals(nodeName)){
version.append(ch,start,length);
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
//开始结束解析某个节点时调用
super.endElement(uri, localName, qName);
if ("app".equals(localName)){
//目前的id,name,version都有可能包含回车或换行符的,所以需要trim一下
Log.d(TAG, "endElement: id = "+id.toString().trim());
Log.d(TAG, "endElement: name = "+name.toString().trim());
Log.d(TAG, "endElement: version = "+version.toString().trim());
//清空stringBuidler
id.setLength(0);
name.setLength(0);
version.setLength(0);
}
}
@Override
public void endDocument() throws SAXException {
//结束整个XML文件解析时调用
super.endDocument();
}
}
在外部使用的代码如下:
try {
SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
XMLReader xmlReader = saxParser.getXMLReader();
//将MyHandler设置到XMLReader中
xmlReader.setContentHandler(new MyHandler());
//开始执行解析
xmlReader.parse(new InputSource(new StringReader(responeString)));
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
SAX解析的原理是加载一点,读取一点,处理一点,对内存要求比较低,适合大容量文件的读取。SAX解析只能从上往下,按顺序读取,不能往回读。
Dom解析的简单用例如下:
try {
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = builder.parse(new ByteArrayInputStream(responeString.getBytes()));
//获取根结点
Element element = doc.getDocumentElement();
//获取app节点列表
NodeList nodeList = element.getElementsByTagName("app");
for (int i = 0; i < nodeList.getLength(); i++) {
//app节点下的子节点
Node item = nodeList.item(i);
NodeList childNodes = item.getChildNodes();
for (int j =0;j<childNodes.getLength();j++) {
Node child = childNodes.item(j);
if (child.getNodeType() == Node.ELEMENT_NODE) {
Element childElement = (Element) child;
//获取节点名称
String nodeName = childElement.getNodeName();
//获取节点内容
String nodeValue = childElement.getFirstChild().getNodeValue();
if ("id".equals(nodeName)) {
Log.d(TAG, "parseXMLWithDom: id = " + nodeValue);
} else if ("name".equals(nodeName)) {
Log.d(TAG, "parseXMLWithDom: name = " + nodeValue);
} else if ("version".equals(nodeName)) {
Log.d(TAG, "parseXMLWithDom: version = " + nodeValue);
}
}
}
}
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
其中还有很多Dom API就不细说了。
Dom解析的原理是一次性加载XML文档到内存,在内存中构建Document树,对内存要求比较高,不适合大容量XML文件读取,容易导致内存溢出。而且Dom解析可以进行任意增删改,可以读取任意位置的数据,甚至可以往回读。
其中JSON解析就是JSONArray和JSONObject的组合,以上述为例:
String jsonString = "[
{
"id": "1",
"name": "Google",
"version": "1.0"
},
{
"id": "2",
"name": "Chrome",
"version": "2.1"
},
{
"id": "3",
"name": "WeChat",
"version": "3.5"
}
]";
//开始解析json字符串
try {
JSONArray jsonArray = new JSONArray(jsonString);
for (int i=0;i<jsonArray.length();i++){
JSONObject jsonObject = jsonArray.getJSONObject(i);
String id = jsonObject.getString("id");
String name = jsonObject.getString("name");
String version = jsonObject.getString("version");
Log.d(TAG, "parseJSON: id =" +id +" name = "+name +" version = "+version);
}
} catch (JSONException e) {
e.printStackTrace();
}
除此之外还有很多第三方框架可以进行json解析,想Gson,fastJson等,这里就不一一叙述了。
=========================================================
对于Gson框架解析JSON数组会稍微麻烦一些,这样我们就可以借助TypeToken将期望转换的类型传入到fromJson中,如下:
List<App> apps = gson.fromJson(responString,new TypeToken<List<App>>(){}.getType())