<html>
<head>
<title> New Document </title>
</head>
<body>
<apps>
<app group="1" category="Map">
<id>13</id>
<name lang="en">Google Maps</name>
<version>1.0</version>
</app>
<app group="2" category="Browser">
<id>14</id>
<name lang="en">Chrome</name>
<version>2.0</version>
</app>
<app group="3" category="Store">
<id>17</id>
<name lang="en">Google Play</name>
<version>2.3</version>
</app>
</apps>
</body>
</html>
Pull解析方式
PULL解析器的运行方式和SAX类似,都是基于事件的模式。不同的是,在PULL解析过程中,我们需要自己获取产生的事件然后做相应的操作,而不像SAX那样由处理器触发一种事件的方法,执行我们的代码。PULL解析器小巧轻便,解析速度快,简单易用,非常适合在Android移动设备中使用,Android系统内部在解析各种XML时也是用PULL解析器。
PULL是顺序扫描XML的每一行,并且根据扫描到的事件来做出不同的行为
PULL一共有5种事件类型:
* START_DOCUMENT = 0:文档的开始,解析器尚未读取任何输入。
* END_DOCUMENT = 1:文档的结束。
* START_TAG = 2:开始标签的解析。
* END_TAG = 3:结束标签的解析。
* TEXT:标签内元素的内容解析。
public class MainActivity extends AppCompatActivity {
private Button pullXml;
private TextView responseText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pullXml = (Button) findViewById(R.id.pull_xml);
responseText = (TextView) findViewById(R.id.response);
pullXml.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new Thread(new Runnable() {
@Override
public void run() {
try{
OkHttpClient client = new OkHttpClient();
//复制IPv4地址
Request request = new Request.Builder()
.url("http://xxx.xxx.x.xxx:8080/server/data.xml")
.build();
Response response = client.newCall(request).execute();
String responseData = response.body().string();
parseXMLWithPull(responseData);
} catch (IOException e){
e.printStackTrace();
}
}
}).start();
}
});
}
private void parseXMLWithPull(String xmlData){
StringBuilder dataBuider = new StringBuilder();
try{
//解析工厂
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
//解析器
XmlPullParser xmlPullParser = factory.newPullParser();
xmlPullParser.setInput(new StringReader(xmlData));
//得到一个解析事件
int eventType = xmlPullParser.getEventType();//0
//如果当前解析事件不等于XmlPullParser.END_DOCUMENT 说明解析工作没有完成
while(eventType != XmlPullParser.END_DOCUMENT){//1
String nodeName = xmlPullParser.getName();
switch (eventType){
//开始解析某个节点
case XmlPullParser.START_TAG://2
if("title".equals(nodeName)){
dataBuider.append("title: " + xmlPullParser.nextText() + "\n");
} else if("app".equals(nodeName)){
dataBuider.append("app group: " + xmlPullParser.getAttributeValue(null,"group") +
" category: " + xmlPullParser.getAttributeValue(null,"category") + "\n");
} else if("id".equals(nodeName)){
dataBuider.append("id: " + xmlPullParser.nextText() + "\n");
} else if("name".equals(nodeName)){
dataBuider.append( "lang: " + xmlPullParser.getAttributeValue(null,"lang") +
" name: " + xmlPullParser.nextText() + "\n" );
} else if("version".equals(nodeName)){
dataBuider.append("version: " + xmlPullParser.nextText() + "\n");
}
break;
//完成解析某个节点
case XmlPullParser.END_TAG://3
if("app".equals(nodeName)){
dataBuider.append("\n");
}
}
//调用next()方法可以获取下一个解析事件
eventType = xmlPullParser.next();
}
showResponse(dataBuider.toString());
//START_DOCUMENT = 0
//END_DOCUMENT = 1
//START_TAG = 2
//END_TAG = 3
}catch (Exception e){
e.printStackTrace();
}
}
public void showResponse(final String response){
runOnUiThread(new Runnable() {
@Override
public void run() {
responseText.setText(response);
}
});
}
}
SAX解析方式
一、概述
SAX,全称Simple API for XML,是一种以事件驱动的XMl API,是XML解析的一种新的替代方法,解析XML常用的还有DOM解析,PULL解析(Android特有),SAX与DOM不同的是它边扫描边解析,自顶向下依次解析,由于边扫描边解析,所以它解析XML具有速度快,占用内存少的优点,对于Android等CPU资源宝贵的移动平台来说是一个巨大的优势。
SAX的优点:
1.解析速度快
2.占用内存少
SAX的缺点:
1.无法知道当前解析标签(节点)的上层标签,及其嵌套结构,仅仅知道当前解析的标 签的名字和属性,要知道其他信息需要程序猿自己编码
2.只能读取XML,无法修改XML
3.无法随机访问某个标签(节点)
SAX解析适用场合:
1.对于CPU资源宝贵的设备,如Android等移动设备
2.对于只需从xml读取信息而无需修改xml
二、SAX解析的步骤
解析步骤很简单,可分为以下四个步骤
1.得到xml文件对应的资源,可以是xml的输入流,文件和uri
2.得到SAX解析工厂(SAXParserFactory)
3.由解析工厂生产一个SAX解析器(SAXParser)
4.传入输入流和handler给解析器,调用parse()解析
ContentHandler.class
public class ContentHandler extends DefaultHandler {
private StringBuilder dataBuider;
private String currentTag;//记录当前解析到的节点名称
//文档解析开始时调用,该方法只会调用一次
@Override
public void startDocument() throws SAXException {
dataBuider= new StringBuilder();
}
/**
* 节点解析开始调用
* @param uri : 命名空间的uri
* @param localName : 标签的名称
* @param qName : 带命名空间的标签名称
*/
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if("app".equals(localName)) {
for(int i = 0; i < attributes.getLength(); i++){
if("group".equals(attributes.getLocalName(i))){
dataBuider.append("app group: " + attributes.getValue(i));
}else if("category".equals(attributes.getLocalName(i))){
dataBuider.append(" category: " + attributes.getValue(i) + "\n");
}
}
}
if("name".equals(localName)){
for(int j = 0; j < attributes.getLength(); j++){
if("lang".equals(attributes.getLocalName(j))){
dataBuider.append("lang: " + attributes.getValue(j) + " ");
}
}
}
//记录当前节点名
currentTag = localName;
}
/**
* 解析标签的内容的时候调用
* @param ch : 当前读取到的TextNode(文本节点)的字节数组
* @param start : 字节开始的位置,为0则读取全部
* @param length : 当前TextNode的长度
* @throws SAXException
*/
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
//根据当前的节点名判断内容添加到哪一个StringBuider对象中
String value = new String(ch, start, length);
if ("id".equals(currentTag)){
dataBuider.append("id: " + value + "\n");
} else if ("name".equals(currentTag)){
dataBuider.append("name: " + value + "\n");
} else if ("version".equals(currentTag)){
dataBuider.append("version: " + value + "\n");
}
}
//标签(节点)解析结束后调用
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
currentTag = null;
}
//文档解析结束后调用,该方法只会调用一次
@Override
public void endDocument() throws SAXException {
super.endDocument();
}
public StringBuilder getXmlData() {
return dataBuider;
}
}
MainActivity.class
public class MainActivity extends AppCompatActivity {
private Button saxXml;
private TextView responseText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
saxXml = (Button) findViewById(R.id.sax_xml);
responseText = (TextView) findViewById(R.id.response);
saxXml.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new Thread(new Runnable() {
@Override
public void run() {
try{
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://xxx.xxx.x.xxx:8080/server/data.xml")
.build();
Response response = client.newCall(request).execute();
String responseData = response.body().string();
parseXMLWithSax(responseData);
} catch (IOException e){
e.printStackTrace();
}
}
}).start();
}
});
}
private void parseXMLWithSax(String xmlData){
try{
//得到SAX解析工厂
SAXParserFactory factory = SAXParserFactory.newInstance();
XMLReader xmlReader = factory.newSAXParser().getXMLReader();
ContentHandler handler = new ContentHandler();
//将ContentHandler 的实例设置到XMLReader中
xmlReader.setContentHandler(handler);
//开始执行解析
xmlReader.parse(new InputSource(new StringReader(xmlData)));
StringBuilder dataBuider= handler.getXmlData();
showResponse(dataBuider.toString());
} catch (Exception e){
e.printStackTrace();
}
}
public void showResponse(final String response){
runOnUiThread(new Runnable() {
@Override
public void run() {
responseText.setText(response);
}
});
}
}
1、xml解析开始,startDocument被调用,这个方法在整个xml解析过程中调用了一次,所以我们可以在这个方法里面初为解析XML做一些准备,比如初始化变量
2、解析每遇到一个标签都会经历startElement-characters-endElement这个过程,即每一个标签都会触发startElement-characters-endElement
3、通过users这个根节点的解析,user标签解析以及name,password标签解析过程,我们知道user标签是users的子标签,name和password标签是user标签的子标签,我们知道当解析一个标签的时候,如果该标签有子标签,则先回调用该标签的startElement方法,这里面可以先得到该标签的属性信息,然后触发characters解析该标签的内容(值),然后子标签触发startElement-characters-endElement(子标签触发),最后该标签触发endElement,该标签解析结束
Dom解析方式
https://yq.aliyun.com/articles/3367
public class MainActivity extends AppCompatActivity {
private Button domXml;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
domXml = (Button) findViewById(R.id.dom_xml);
domXml.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new Thread(new Runnable() {
@Override
public void run() {
try{
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://xxx.xxx.x.xxx:8080/server/data.xml")
.build();
Response response = client.newCall(request).execute();
String responseData = response.body().string();
InputStream in = new ByteArrayInputStream(responseData.getBytes());
parseXMLWithDOM(in);
} catch (IOException e){
e.printStackTrace();
}
}
}).start();
}
});
}
private void parseXMLWithDOM(InputStream xmlData){
try{
//通过抽象工厂类DocumentBuilderFactory的静态方法newInstance获得一个工厂实例对象
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
//通过工厂实例对象获得一个DocumentBuilder对象
DocumentBuilder db = factory.newDocumentBuilder();
//通过DocumentBuilder对象的parse方法加载inputStream文件进行解析转换为Document对象
Document document = db.parse(xmlData);
//通过Document对象的getElementsByTagName()方法获得元素节点集合
NodeList appList = document.getElementsByTagName("app");
//遍历每一个元素节点
int size = appList.getLength();
Log.e("log","一共有" + size + "个节点");
//遍历每个app元素节点
for(int i = 0; i < size;i++){
//通过item方法获取每一个app元素节点
Node appNode = appList.item(i);
//获取app元素节点所有属性节点集合
NamedNodeMap attriMap = appNode.getAttributes();
Log.e("log","第" + (i+1) + "个节点");
int attriSize = attriMap.getLength();
Log.e("log","第" + (i+1) + "个节点一共有" + attriSize + "个属性节点");
//属性
for(int j = 0; j < attriSize ;j++){
//通过item方法获取app元素节点的属性节点
Node attriNode = attriMap.item(j);
Log.e("log",
"属性节点:(" +
" type: " + attriNode.getNodeType() + // 获取属性节点属性类型
" ,name: " + attriNode.getNodeName() + // 获取属性节点属性名称
" ,value: " + attriNode.getNodeValue() +")"); // 获取属性节点属性值
}
//获取app元素节点的子节点集合
NodeList chidNodeList = appNode.getChildNodes();
int childSize = chidNodeList.getLength();
Log.e("log","一共有" + childSize + "个子节点(元素节点和文本节点)");
for(int k =0; k < childSize;k++){
//获取子节点
Node childNode = chidNodeList.item(k);
//区分Elemet类型节点和Text类型节点
if(childNode.getNodeType() == Node.ELEMENT_NODE){//1
//获取子节点所有属性节点集合
NamedNodeMap childAttriMap = childNode.getAttributes();
String type = "";
String name = "";
String value = "";
//属性
for(int m = 0 ; m < childAttriMap.getLength(); m++){
//通过item方法获取子节点的属性节点
Node childAttriNode = childAttriMap.item(m);
type = childAttriNode.getNodeType()+"";
name = childAttriNode.getNodeName()+"";
value = childAttriNode.getNodeValue()+"";
}
Log.e("log",
"元素节点:(" +
" type:" + childNode.getNodeType() + //获取元素子节点类型
" ,name:" + childNode.getNodeName() + //获取元素子节点名称
" ,value:" + childNode.getNodeValue() + " )" + //获取元素子节点值
"属性节点:(" +
" type:" + type +
" ,name:" + name +
" ,value:"+value + " )" +
"文本节点:(" +
" type:" + childNode.getFirstChild().getNodeType() +
" name: " + childNode.getFirstChild().getNodeName() +
" value:" + childNode.getFirstChild().getNodeValue() + " )");
}
}
}
}catch (Exception e){
e.printStackTrace();
}
}
}
递归解析XML
public class MainActivity extends AppCompatActivity {
StringBuilder dataBuider = new StringBuilder();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder db = factory.newDocumentBuilder();
Document document = db.parse(getAssets().open("data.xml"));
//获得根节点
Element root = document.getDocumentElement();
//获取根节点的子节点
NodeList nodeList = root.getChildNodes();
paseXMLWithDOM(nodeList);
Log.e("log", dataBuider.toString());
}catch (Exception e){
e.printStackTrace();
}
}
//解析子节点
private void paseXMLWithDOM(NodeList nodeList){
for(int i = 0; i < nodeList.getLength();i++){
Node node = nodeList.item(i);
//区分Elemet类型节点和Text类型节点
if(node.getNodeType() == Node.ELEMENT_NODE){
//获取元素节点名和值
String nodeName = node.getNodeName();
String nodeValue = node.getFirstChild().getNodeValue();
//获取元素节点的属性节点
NamedNodeMap attrMap = node.getAttributes();
if(attrMap.getLength()>0){
//元素节点包含属性节点
StringBuilder attrBuilder = new StringBuilder();
for(int j = 0; j < attrMap.getLength();j++){
Node attrNode = attrMap.item(j);
attrBuilder.append(attrNode.getNodeName()+":\""+attrNode.getNodeValue()+"\" ");
}
dataBuider.append(new String("<"+nodeName +" "+ attrBuilder.toString()+">"+nodeValue).trim());
} else {
//元素节点没有包含属性节点
dataBuider.append(new String("<"+nodeName+ ">"+nodeValue).trim());
}
//如果dataBuider是 > 结尾就换行
if(dataBuider.lastIndexOf(">") == dataBuider.length()-1){
dataBuider.append("\n");
}
//元素节点是否有子节点
if(node.hasChildNodes()) {
//递归解析元素节点的子节点
paseXMLWithDOM(node.getChildNodes());
}
dataBuider.append("</"+nodeName+">"+"\n");
}
}
}
}