好久不碰XML解析,在此写了个小的demo来温习一下解析XML,其中我用的sax来解析XML,XML文件存放在tomcat服务器,由安卓端通过http的GET请求获取到xml,之后就是解析啦,解析完将所有数据存放在实体类中,接下来就是将数据显示在ListView上面了。demo虽小,但其中遇到一些问题卡住我半天,下面就来看看吧。服务器的搭建比较简单了,其中在servlet中处理安卓端的get请求,将book.xml以字节流的方式返回给安卓端</span>
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.Properties;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HTTPServlet extends HttpServlet {
String data;
/**
* Constructor of the object.
*/
public HTTPServlet() {
super();
}
/**
* Destruction of the servlet. <br>
*/
public void destroy() {
super.destroy(); // Just puts "destroy" string in log
// Put your code here
}
/**
* The doGet method of the servlet. <br>
*
* This method is called when a form has its tag value method equals to get.
*
* @param request
* the request send by the client to the server
* @param response
* the response send by the server to the client
* @throws ServletException
* if an error occurred
* @throws IOException
* if an error occurred
*/
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
String name=request.getParameter("name");
String password=request.getParameter("password");
InputStream inputStream=HTTPServlet.class.getClassLoader().getResourceAsStream("books.xml");
if(inputStream!=null){
StringBuilder sb=new StringBuilder();
BufferedReader reader=new BufferedReader(new InputStreamReader(inputStream,"utf-8"));
while((data=reader.readLine())!=null){
sb.append(data);
}
out.print(sb.toString());
System.out.println(sb.toString());
}else {
out.print("input null");
}
}
/**
* The doPost method of the servlet. <br>
*
* This method is called when a form has its tag value method equals to
* post.
*
* @param request
* the request send by the client to the server
* @param response
* the response send by the server to the client
* @throws ServletException
* if an error occurred
* @throws IOException
* if an error occurred
*/
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
/**
* Initialization of the servlet. <br>
*
* @throws ServletException
* if an error occurs
*/
public void init() throws ServletException {
// Put your code here
}
}
再回到安卓端,安卓端主要处理的就是xml文件的解析,下面一步步说下所有流程
首先,看看xml文件
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book id="1">
<name>冰与火之歌</name>
<author>乔治马丁</author>
<year>2014</year>
<price>89</price>
</book>
<book id="2">
<name>安徒生童话</name>
<year>2004</year>
<price>77</price>
<language>English</language>
</book>
</bookstore>
我们创建与之对应的实体类Book
import java.io.Serializable;
public class Book implements Serializable {
private static final long serialVersionUID = 1L;
private String id;
private String name;
private String price;
private String author;
private String year;
private String language;
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getYear() {
return year;
}
public void setYear(String year) {
this.year = year;
}
public static long getSerialversionuid() {
return serialVersionUID;
}
@Override
public String toString() {
return "Book [id=" + id + ", name=" + name + ", price=" + price
+ ", author=" + author + ", year=" + year + ", language="
+ language + "]";
}
}
所有属性的set,get方法自行填补
以下的ParseXML类主要负责通过HTTP协议获取服务器返回数据流
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import android.R.xml;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.widget.ListView;
import android.widget.TextView;
public class ParseXML extends Thread {
public static final int SHOW_LISTVIEW=0x11;
private String path;
private Handler handler;
private List<Book> books;
public ParseXML(String path) {
this.path=path;
}
public ParseXML(String path, Handler handler) {
this.path = path;
this.handler = handler;
}
@Override
public void run() {
super.run();
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
conn.setReadTimeout(5000);
//获取服务器返回的输入流
String date = getXMLInputStream(conn.getInputStream());
SAXParserFactory factory=SAXParserFactory.newInstance();
XMLReader xmlreader=factory.newSAXParser().getXMLReader();
ParseXMLHandler parse=new ParseXMLHandler();
xmlreader.setContentHandler(parse);
xmlreader.parse(new InputSource(new StringReader(date.toString())));
books= parse.getAllBooks();
Log.i("book_lenth", books.size()+"");
Log.i("books", books.toString()+"");
Message message=Message.obtain();
message.what=SHOW_LISTVIEW;
message.obj=books;
handler.sendMessage(message);
conn.disconnect();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public String getXMLInputStream(InputStream inputStream) {
BufferedReader reader;
StringBuilder sb = new StringBuilder();
String str;
try {
reader = new BufferedReader(new InputStreamReader(inputStream,
"utf-8"));
if (reader != null) {
while ((str = reader.readLine()) != null) {
sb.append(str);
}
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}
public List<Book> getBooks(){
return books;
}
}
ParseXMLHandler类主要负责用SAX方式解析xml文件
SAX与DOM比较而言,SAX是一种轻量型的方法。我们知道,在处理DOM的时候,我们需要读入整个的XML文档,然后在内存中创建DOM树,生成DOM树上的每个Node对象。当文档比较小的时候,这不会造成什么问题,但是一旦文档大起来,处理DOM就会变得相当费时费力。特别是其对于内存的需求,也将是成倍的增长,以至于在某些应用中使用DOM是一件很不划算的事(比如在applet中)。这时候,一个较好的替代解决方法就是SAX。
SAX在概念上与DOM完全不同。它不同于DOM的文档驱动,它是事件驱动的,它并不需要读入整个文档,而文档的读入过程也就是SAX的解析过程。所谓事件驱动,是指一种基于回调(callback)机制的程序运行方法。
startDocument()和endDocument()在开始和结束解析xml文件时调用,可用于家在一些配置
startElement()和endElement()在解析开始元素和结束元素时候调用
characters()是对元素的值的获取,其中不要忽略xml文件空白处此方法也会被调用,但获取的值均为空
import java.util.ArrayList;
import java.util.List;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import android.util.Log;
public class ParseXMLHandler extends DefaultHandler {
private List<Book> books;
private Book book;
private String qname;
String data = null;
public ParseXMLHandler() {
books = new ArrayList<Book>();
}
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
super.characters(ch, start, length);
if(qname!=null){
data = new String(ch, start, length);
if ("name".equals(qname)) {
book.setName(data);
} else if ("author".equals(qname)) {
book.setAuthor(data);
} else if ("price".equals(qname)) {
book.setPrice(data);
} else if ("year".equals(qname)) {
book.setYear(data);
} else if ("language".equals(qname)) {
book.setLanguage(data);
}
}
}
@Override
public void endDocument() throws SAXException {
super.endDocument();
// Log.i("TAG", "stop parse xml");
}
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
super.endElement(uri, localName, qName);
//此处用qName而不用qname!!!!!!!!!
if ("book".equals(qName)) {
books.add(book);
book = null;
}
qname=null;
}
@Override
public void startDocument() throws SAXException {
super.startDocument();
// Log.i("TAG", "start parse xml");
}
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
super.startElement(uri, localName, qName, attributes);
if (qName.equals("book")) {
book = new Book();
int num = attributes.getLength();
// 遍历所有的属性
for (int i = 0; i < num; i++) {
String name = attributes.getQName(i);
String value = attributes.getValue(i);
if ("id".equals(name)) {
book.setId(value);
}
}
}
this.qname = qName;
}
public List<Book> getAllBooks() {
return books;
}
}
接下来就是MainActivity的简单说明,MainActivity主要用于显示xml解析出来的数据,其中封装在集合中的数据要通过Handler来发送给主线程,让主线程来更新UI,因为只有主线程才可以更新UI,子线程更新UI是不安全的。
import java.util.List;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
public class MainActivity extends Activity implements OnClickListener {
private Button bt_get;
private ListView listView;
private BookAdapter adapter;
private List<Book> books;
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case ParseXML.SHOW_LISTVIEW:
books = (List<Book>) msg.obj;
adapter = new BookAdapter(MainActivity.this, books);
listView.setAdapter(adapter);
break;
default:
break;
}
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bt_get = (Button) findViewById(R.id.bt_get);
listView = (ListView) findViewById(R.id.listView);
bt_get.setOnClickListener(this);
}
@Override
public void onClick(View v) {
ParseXML parseXML = new ParseXML(
"http://192.168.1.118:8080/androidService/servlet/HTTPServlet",
handler);
parseXML.start();
books = parseXML.getBooks();
// 不能在此处更新UI,其中问题在于,ParseXML类去解析服务器返回的流是在子线程中进行,
// 直接在此处给adapter设置books,会获取不到books的集合,简单做法就是,子线程将数据通过handler发送给主线程来更新UI
// if(books!=null){
// adapter = new BookAdapter(this, books);
// listView.setAdapter(adapter);
// }else {
// Log.i("TAG", "books is null");
// }
}
}
最后adapter和布局文件就不贴上来了,比较简单。
demo下载地址点击打开链接