Android之PULL、SAX、DOM解析XML
標簽: 筆記 感悟 java dom xml android studio android
背景:解析天氣預報的xml文件,在模擬器顯示
解析前準備
layout目錄下weather.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/beijing2"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_city"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignEnd="@+id/tv_weather"
android:layout_alignRight="@+id/tv_weather"
android:layout_alignParentTop="true"
android:layout_marginTop="39dp"
android:text="上海"
android:textSize="50sp" />
<ImageView
android:id="@+id/iv_icon"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_below="@+id/tv_city"
android:layout_alignStart="@+id/ll_btn"
android:layout_alignLeft="@+id/ll_btn"
android:layout_marginStart="44dp"
android:layout_marginLeft="44dp"
android:layout_marginTop="42dp"
android:paddingBottom="5dp"
android:src="@mipmap/ic_launcher" />
<TextView
android:id="@+id/tv_weather"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/iv_icon"
android:layout_alignRight="@+id/iv_icon"
android:layout_marginTop="18dp"
android:layout_marginRight="15dp"
android:gravity="center"
android:text="多云"
android:textSize="18sp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/iv_icon"
android:layout_marginStart="39dp"
android:layout_marginLeft="39dp"
android:layout_toEndOf="@+id/iv_icon"
android:layout_toRightOf="@+id/iv_icon"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/tv_temp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:gravity="center_vertical"
android:text="-7℃"
android:textSize="22sp" />
<TextView
android:id="@+id/tv_wind"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="風力:3級"
android:textSize="18sp" />
<TextView
android:id="@+id/tv_pm"
android:layout_width="73dp"
android:layout_height="wrap_content"
android:text="pm"
android:textSize="18sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:orientation="horizontal">
<Button
android:id="@+id/btn_bj"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="北京" />
<Button
android:id="@+id/btn_sh"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="上海" />
<Button
android:id="@+id/btn_gz"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="廣州" />
</LinearLayout>
</RelativeLayout>
需要解析的文件:raw目錄下的weather1.xml
<?xml version ="1.0" encoding ="utf-8"?>
<infos>
<city id="sh">
<temp>20℃/30℃</temp>
<weather>晴天多云</weather>
<name>上海</name>
<pm>80</pm>
<wind>1級</wind>
</city>
<city id="bj">
<temp>26℃/32℃</temp>
<weather>晴天</weather>
<name>北京</name>
<pm>98</pm>
<wind>3級</wind>
</city>
<city id="gz">
<temp>15℃/24℃</temp>
<weather>多云</weather>
<name>廣州</name>
<pm>30</pm>
<wind>5級</wind>
</city>
</infos>
WeatherBean.java
package com.example.ch4;
public class WeatherBean {
private String id;
private String temp;
private String weather;
private String name;
private String pm;
private String wind;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTemp() {
return temp;
}
public void setTemp(String temp) {
this.temp = temp;
}
public String getWeather() {
return weather;
}
public void setWeather(String weather) {
this.weather = weather;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPm() {
return pm;
}
public void setPm(String pm) {
this.pm = pm;
}
public String getWind() {
return wind;
}
public void setWind(String wind) {
this.wind = wind;
}
}
MainActivity.java將weather1.xml解析到的信息映射到布局weather.xml中
//解析天氣
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private TextView tvCity;
private TextView tvWeather;
private TextView tvTemp;
private TextView tvWind;
private TextView tvPm;
private ImageView ivIcon;
private Map<String, String> map;
private List<Map<String, String>> list;
private String temp, weather, name, pm, wind;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.weather);
// 初始化文本控件
initView();
try {
// 讀取 weather1.xml 文件
InputStream is = this.getResources().openRawResource(R.raw.weather1);
// 把每個城市的天氣信息集合存到 weatherInfos 中
// List<WeatherInfo> weatherInfos = PULLUnit.getInfosFromXML(is);
// List<WeatherInfo> weatherInfos = SAXContentHandler.parse(is);
List<WeatherBean> weatherInfos = DOMUnit.parseXmlByDom(is);
// 循環讀取 weatherInfos 中的每一條數據
list = new ArrayList<Map<String, String>>();
for (WeatherBean info : weatherInfos) {
map = new HashMap<String, String>();
map.put("temp", info.getTemp());
map.put("weather", info.getWeather());
map.put("name", info.getName());
map.put("pm", info.getPm());
map.put("wind", info.getWind());
list.add(map);
}
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(this, "解析信息失敗", Toast.LENGTH_SHORT).show();
}
// 自定義 getMap() 方法,顯示天氣信息到文本控件中,默認顯示北京的天氣
getMap(1, R.drawable.wanzi);
}
private void initView() {
tvCity = (TextView) findViewById(R.id.tv_city);
tvWeather = (TextView) findViewById(R.id.tv_weather);
tvTemp = (TextView) findViewById(R.id.tv_temp);
tvWind = (TextView) findViewById(R.id.tv_wind);
tvPm = (TextView) findViewById(R.id.tv_pm);
ivIcon = (ImageView) findViewById(R.id.iv_icon);
findViewById(R.id.btn_sh).setOnClickListener(this);
findViewById(R.id.btn_bj).setOnClickListener(this);
findViewById(R.id.btn_gz).setOnClickListener(this);
}
@Override
public void onClick(View v) {
// 按鈕的點擊事件
switch (v.getId()) {
case R.id.btn_sh:
getMap(0, R.drawable.wanzi);
break;
case R.id.btn_bj:
getMap(1, R.drawable.wanzi);
break;
case R.id.btn_gz:
getMap(2, R.drawable.wanzi);
break;
}
}
// 將城市天氣信息分條展示到界面上
private void getMap(int number, int iconNumber) {
Map<String, String> cityMap = list.get(number);
temp = cityMap.get("temp");
weather = cityMap.get("weather");
name = cityMap.get("name");
pm = cityMap.get("pm");
wind = cityMap.get("wind");
tvCity.setText(name);
tvWeather.setText(weather);
tvTemp.setText("" + temp);
tvWind.setText("風力 : " + wind);
tvPm.setText("pm: " + pm);
ivIcon.setImageResource(iconNumber);
}
}
PULL解析
PULL解析器的運行方式和SAX類似,都是基于事件的模式。不同的是,在PULL解析過程中,需要自己獲取產生的事件然后做相應的操作,而不像SAX那樣由處理器觸發一種事件的方法,執行我們的代碼。PULL解析器小巧輕便,解析速度快,簡單易用,非常適合在Android移動設備中使用,Android系統內部在解析各種XML時也是用PULL解析器。
一個一個標簽(tag)的去解析,至上而下解析到文件的末尾,因為xml文件里面的格式相似度高,可以用到while循環,沒有解析到文件的末尾之前一直解析。
循環中要加上循環終止的條件的變化語句:type = parser.next()
解析完所有變量之后把常量置為null,節省內存,方便垃圾清理器回收對象。
步驟:
? 1.實例化解析器:XmlPullParser parser = Xml.newPullParser();
? 2.初始化解析器:parser.setInput(is, “utf-8”);
? 3.獲取當前事件的類型通過循環解析標簽
public class PULLUnit {
// 解析 xml 文件返回天氣信息的集合
public static List<WeatherBean> getInfosFromXML(InputStream is) throws Exception {
// 得到 pull 解析器
XmlPullParser parser = Xml.newPullParser();
// 初始化解析器 , 第一個參數代表包含 xml 的數據
parser.setInput(is, "utf-8");
List<WeatherBean> weatherInfos = null;
WeatherBean weatherInfo = null;
// 得到當前事件的類型
int type = parser.getEventType();
// END_DOCUMENT 文檔結束標簽
while (type != XmlPullParser.END_DOCUMENT) {
switch (type) {
// 一個節點的開始標簽
case XmlPullParser.START_TAG:
// 解析到全局開始的標簽 infos 根節點
if ("infos".equals(parser.getName())) {
weatherInfos = new ArrayList<WeatherBean>();
} else if ("city".equals(parser.getName())) {
weatherInfo = new WeatherBean();
String idStr = parser.getAttributeValue(0);
weatherInfo.setId(idStr);
} else if ("temp".equals(parser.getName())) {
//parset.nextText() 得到該 tag 節點中的內容
String temp = parser.nextText();
weatherInfo.setTemp(temp);
} else if ("weather".equals(parser.getName())) {
String weather = parser.nextText();
weatherInfo.setWeather(weather);
} else if ("name".equals(parser.getName())) {
String name = parser.nextText();
weatherInfo.setName(name);
} else if ("pm".equals(parser.getName())) {
String pm = parser.nextText();
weatherInfo.setPm(pm);
} else if ("wind".equals(parser.getName())) {
String wind = parser.nextText();
weatherInfo.setWind(wind);
}
break;
// 一個節點結束的標簽
case XmlPullParser.END_TAG:
// 一個城市的信息處理完畢, city 的結束標簽
if ("city".equals(parser.getName())) {
weatherInfos.add(weatherInfo);//把城市信息加到信息集合里面
weatherInfo = null;//清空數據,便于回收變量
}
break;
}
type = parser.next();
}
return weatherInfos;
}
}
SAX解析
SAX(Simple API for XML)解析器是一種基于事件的解析器,它的核心是事件處理模式,主要是圍繞著事件源以及事件處理器來工作的。當事件源產生事件后,調用事件處理器相應的處理方法,一個事件就可以得到處理。在事件源調用事件處理器中特定方法的時候,還要傳遞給事件處理器相應事件的狀態信息,這樣事件處理器才能夠根據提供的事件信息來決定自己的行為。
簡單地說就是對文檔進行順序掃描,當掃描到文檔(document)開始與結束、元素(element)開始與結束、文檔(document)結束等地方時通知事件處理函數,由事件處理函數做相應動作,然后繼續同樣的掃描,直至文檔結束。
SAX解析器的優點是解析速度快,占用內存少。非常適合在Android移動設備中使用。
步驟:
1.自定義類去繼承Android提供的ContextHandler接口的實現類DefaultHandler。
2.實例化一個SAXParserFactory對象:
? SAXParserFactory factory = SAXParserFactory.newInstance();
3.通過factory對象獲得一個SAXParser對象,該對象就稱做SAX 解析器:
? SAXParser saxParser = factory.newSAXParser();
4.saxParser對象調用parse方法解析XML文件:
? saxParser.parse(File file,DefaultHandler dh);
5.ContextHandler接口常用方法解析
- startDocument() :讀取文檔開頭時調用,可在此方法中進行預處理操作,比如:初始化bean類,或者容器
- startElement() : 開始標簽時觸發(此處獲取屬性值)
- characters() : 處理文件中讀取到內容,即標簽間內容
- endElment() : 結束標簽時觸(此處獲取標簽的文本值)
- endDocument() : 讀取文檔結束時調用,在此方法中進行結尾工作
public class SAXContentHandler extends DefaultHandler {
private List<WeatherBean> WeatherList = new ArrayList<>();//解析集合
private WeatherBean WeatherBean;//記錄當前messageBean
private String curTagName; //通過此變量,記錄當前標簽的名稱
/**
* 提供給外部調用的方法
*
* @param inputStream 輸入流
* @return 解析的實體類集合
*/
public static List<WeatherBean> parse(InputStream inputStream) {
try {
//創建SAXParserFactory解析工廠類
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXContentHandler saxUtil = new SAXContentHandler();
//實例化一個SAXParser解析類
SAXParser saxParser = factory.newSAXParser();
//開始解析文檔
//參數2:利用我們定義的handler進行解析輸入的文檔
saxParser.parse(inputStream, saxUtil);
return saxUtil.getUserBeanList();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private SAXContentHandler() {
}
private List<WeatherBean> getUserBeanList() {
return WeatherList;
}
//開始解析文檔,做初始化工作
@Override
public void startDocument() throws SAXException {
super.startDocument();
WeatherList = new ArrayList<>();
// Log.e("messageInfo", "解析開始");
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
super.startElement(uri, localName, qName, attributes);
if (qName.trim().equals("city")) {
WeatherBean = new WeatherBean();
WeatherBean.setName(qName.trim());
//根據屬性名拿到值
String id = String.valueOf(attributes.getValue("id"));
WeatherBean.setId(id);
}
}
//獲取元素值
//解析字符
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
super.characters(ch, start, length);
//根據char數組生產一個字符串
curTagName = new String(ch, start, length);
}
//遍歷結束標簽
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
super.endElement(uri, localName, qName);
//解析子標簽
if (qName.trim().equals("city")) {
//當一個用戶被解析完,加入到集合中
WeatherList.add(WeatherBean);
WeatherBean = null;
} else if (qName.trim().equals("id"))
WeatherBean.setId(curTagName);
else if (qName.trim().equals("temp"))
WeatherBean.setTemp(curTagName);
else if (qName.trim().equals("weather"))
WeatherBean.setWeather(curTagName);
else if (qName.trim().equals("name"))
WeatherBean.setName(curTagName);
else if (qName.trim().equals("pm"))
WeatherBean.setPm(curTagName);
else if (qName.trim().equals("wind"))
WeatherBean.setWind(curTagName);
}
//文檔結束
@Override
public void endDocument() throws SAXException {
super.endDocument();
// Log.e("messageInfo", "解析結束");
}
}
DOM解析
DOM是基于樹形結構的的節點或信息片段的集合,允許開發人員使用DOM API遍歷XML樹、檢索所需數據。分析該結構通常需要加載整個文檔和構造樹形結構,然后才可以檢索和更新節點信息。
由于DOM在內存中以樹形結構存放,因此檢索和更新效率會更高。但是對于特別大的文檔,解析和加載整個文檔將會很耗資源。
步驟:
1.建立DocumentBuilderFactor,用于獲得DocumentBuilder對象:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
2.建立DocumentBuidler:
DocumentBuilder builder = factory.newDocumentBuilder();
3.建立Document對象,獲取樹的入口:
Document doc = builder.parse(“xml文件的相對路徑或者絕對路徑”);
4.建立NodeList:
NodeList n1 = doc.getElementByTagName(“讀取節點”);
5.進行xml信息獲取
此時的List包含三個city節點
public class DOMUnit {
/**
* 根據dom解析XML文檔
*
* @return 返回解析后的集合,可能為空
* @paraminputStream需要解析的輸入流
*/
public static List<WeatherBean> parseXmlByDom(InputStream input) {
List<WeatherBean> itList = new ArrayList<>();
try {
//一系列的初始化
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
//獲得Document對象
Document document = builder.parse(input);
//獲得List(本xml中就是根結點)
NodeList manList = document.getElementsByTagName("city");
//遍歷標簽
for (int i = 0; i < manList.getLength(); i++) {
//獲得標簽
Node node = manList.item(i);
//獲得標簽里面的標簽
NodeList childNodes = node.getChildNodes();
//新建對象
WeatherBean WeatherBean2 = new WeatherBean();
//取出屬性值
String id = node.getAttributes().getNamedItem("id").getNodeValue();
WeatherBean2.setId(id);
//遍歷city標簽里面的標簽
for (int j = 0; j < childNodes.getLength(); j++) {
Node childNode = childNodes.item(j);
if ("temp".equals(childNode.getNodeName())) {
String temp = childNode.getTextContent();
WeatherBean2.setTemp(temp);
} else if ("weather".equals(childNode.getNodeName())) {
String weather = childNode.getTextContent();
WeatherBean2.setWeather(weather);
} else if ("name".equals(childNode.getNodeName())) {
String name = childNode.getTextContent();
WeatherBean2.setName(name);
} else if ("pm".equals(childNode.getNodeName())) {
String pm = childNode.getTextContent();
WeatherBean2.setPm(pm);
} else if ("wind".equals(childNode.getNodeName())) {
String wind = childNode.getTextContent();
WeatherBean2.setWind(wind);
}
}
//加到List中
itList.add(WeatherBean2);
}
} catch (ParserConfigurationException | IOException |
SAXException e) {
e.printStackTrace();
}
return itList;
}
}
結果
三種解析方式區別:
(1)
SAX解析器的優點是解析速度快,占用內存少。
DOM在內存中以樹形結構存放,因此檢索和更新效率會更高。但是對于特別大的文檔,解析和加載整個文檔將會很耗資源。
PULL解析器的運行方式和SAX類似,都是基于事件的模式。PULL解析器小巧輕便,解析速度快,簡單易用。
(2)
DOM,它是生成一個樹,有了樹以后你搜索、查找都可以做。
SAX和PULL是基于流的,就是解析器從頭到尾解析一遍xml文件,解析完了以后你不過想再查找重新解析。
SAX和PULL的區別:
(1)sax的原理是解析器解析過程中通過回調把tag/value值等傳給你,你可以比較、操作。
而pull的原理是它只告訴你一個tag開始或者結束了,至于tag/value的值是什么需要你自己去向parser問,所以叫做pull,而sax看起來 是push給你的。
(2)如果在一個XML文檔中我們只需要前面一部分數據,但是使用SAX方式或DOM方式會對整個文檔進行解析,盡管XML文檔中后面的大部分數據我們其實都不需要解析,因此這樣實際上就浪費了處理資源。使用PULL方式正合適。
Pull解析器和SAX解析器雖有區別但也有相似性。他們的區別為:SAX解析器的工作方式是自動將事件推入注冊的事件處理器進行處理,因此你不能控制事件的處理主動結束;而Pull解析器的工作方式為允許你的應用程序代碼主動從解析器中獲取事件,正因為是主動獲取事件,因此可以在滿足了需要的條件后不再獲取事件,結束解析。也就是說pull是一個while循環,隨時可以跳出,而sax不是,sax是只要解析了,就必須解析完成,在解析過程中在讀取到特定tag時調用相應處理事件。這是他們主要的區別。
而他們的相似性在運行方式上,Pull解析器也提供了類似SAX的事件,開始文檔START_DOCUMENT和結束文檔END_DOCUMENT,開始元素START_TAG和結束元素END_TAG,遇到元素內容TEXT等,但需要調用next() 方法提取它們(主動提取事件)。
智能推薦
android使用sax解析xml
隨著技術的發展,現在的web已經和以前不同了。web已經逐漸像移動的方向傾斜,作為程序員的確應該拓展一下自己的知識層面。學習各方面的知識,今天就接著前幾天的弄一下android的xml解析,這次就使用sax的方式解析xml.下面就一步一步的來做吧。 1.編寫一個簡單的xml 2.編寫pojo類 3.寫一個解析xml的類 4.進行單元測試 最后來看一下運行效果圖,這里最好弄個filter,控制臺就沒...
XML的DOM和SAX解析方式
昨天抽取并解析了一大批從微信鉤子收取到的小程序消息,它們都是用很復雜的XML表示的。平常不是很接觸XML,本文就隨便說說XML的兩種解析方式。 DOM解析方式 DOM即文檔對象模型(document object model)。根據W3C的描述,DOM是一套用于HTML和XML文檔的標準接口,它定義了文檔的邏輯結構,以及訪問或操作文檔的方式。 DOM Parser會將文檔解析為包含元素、屬性和文本...
XML解析___使用Dom or使用Sax
xml解析方式分為兩種,dom和sax dom:(Document Object Model,即對文檔對象模型)是W3C組織推薦的處理XML的一種方式 Sax:(Simple API for XML)不是官方標準,但它是xml社區事實上的標準,幾乎所有的xml解析器都支持它。 XML解析開發包 Jaxp、Jdom、dom4J 使用DOM解析XML介紹 DOM...
XML學習筆記(一)之DOM及SAX方式解析XML原理
1.XML XML 指可擴展標記語言(eXtensible Markup Language)。XML 被設計用來傳輸和存儲數據。 (1)XML 文檔形成了一種樹結構,它從"根部"開始,然后擴展到"枝葉"。 示例: &nb...
猜你喜歡
XML之SAX解析XML實例
--------------------------------------------XML之SAX解析XML------------------------------------------ 一,前言 SAX是針對DOM解析XML內存占用大,查找速度慢的缺點而出現的解決方案。SAX解析器對XML文檔解析會從XML文檔開始位置起進行解析,同時根據已經定義好的事件處理器,來解決當前所解析的部分(...
freemarker + ItextRender 根據模板生成PDF文件
1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...
電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!
Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...
requests實現全自動PPT模板
http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...