对XML及Json数据的操作

时间:2022-10-16 20:54:46

对XML和Json数据的操作方法,分别记录一种方法:

一、对XML数据进行操作

Android平台对XML解析,主要有三种方式:Simple  API for XML(SAX)、Document Object Model(DOM)、android自带的PULL解析器解析XML文件。

需要解析的XML文件是:

<?xml version="1.0" encoding="UTF-8"?>
<books>
<book id="12" add="a">
<name>thinking in java</name>
<price>85.5</price>
</book>
<book id="15" add="b">
<name>Spring in Action</name>
<price>39.0</price>
</book>
</books>

定义一个JavaBean,用于保存上面解析出来的XML文件内容:

public class Book {
private int id;
private String name;
private float price;
private String add;
public String getAdd(){
return this.add;
}
public void setAdd(String add){
this.add=add;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
@Override
public String toString(){
return this.id+":"+this.name+":"+this.price+":"+this.add;
}
}
(一)、使用SAX方式解析XML文件

SAX是事件驱动的形式,SAX会判断当前解析的内容是否符合XML语法的某部分,如果符合就触发事件,也就会调用回调方法。

public class SaxParseService extends DefaultHandler{
private List<Book> books = null;
private Book book = null;
private String preTag = null;//作用是记录解析时的上一个节点名称

/**
* 进行XML文件解析
* @param xmlStream XML文件的输入流
* @return 返回解析出来的XML文件内容
* @throws Exception
*/
public List<Book> getBooks(InputStream xmlStream) throws Exception{
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
SaxParseService handler = new SaxParseService();
parser.parse(xmlStream, handler);
return handler.getBooks();
}

public List<Book> getBooks(){
return books;
}

/**
* 解析到开始XML文档标签时回调的方法,比如解析到<?xml version...
*/
@Override
public void startDocument() throws SAXException {
books = new ArrayList<Book>();
}

/**
* 解析到开始元素标签的时候回调的方法,比如解析到<book>
* qName表示元素标签的名称
* attributes表示元素标签的属性,下表从0开始
*
*/
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if("book".equals(qName)){
book = new Book();
book.setId(Integer.parseInt(attributes.getValue(0)));
book.setAdd(attributes.getValue(1));
}
preTag = qName;//将正在解析的节点名称赋给preTag
}

/**
* 解析到结束元素标签的时候回调的方法,比如解析到</book>
* qName表示元素标签的名称
*/
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
if("book".equals(qName)){
books.add(book);
book = null;
}
preTag = null;/**当解析结束时置为空。这里很重要,例如,当图中画3的位置结束后,会调用这个方法
,如果这里不把preTag置为null,根据startElement(....)方法,preTag的值还是book,当文档顺序读到图
中标记4的位置时,会执行characters(char[] ch, int start, int length)这个方法,而characters(....)方
法判断preTag!=null,会执行if判断的代码,这样就会把空值赋值给book,这不是我们想要的。*/
}

/**
* 处理解析XML文件时得到的内容,即TextNode
* 使用new String(ch,start,length)可以取得该内容
*/
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
if(preTag!=null){
String content = new String(ch,start,length);
if("name".equals(preTag)){
book.setName(content);
}else if("price".equals(preTag)){
book.setPrice(Float.parseFloat(content));
}
}
}

}



观察上面工具类的方法,主要是由判断XML语法结构的回调方法构成,还是很好理解的。

File file=new File("d:"+File.separator+"book.xml");
InputStream in=new FileInputStream(file);
SaxParseService sps=new SaxParseService();
List<Book> list=sps.getBooks(in);
System.out.println(list);


(二)、使用DOM方式解析XML文件

DOM的方式解析XML文件会将整个XML文件加载到内存中,方便对整个XML树的检索,但是内存消耗大,如果XML文件较大,还是推荐使用SAX方式解析。

public class DOMParseService {
public static List<Book> getBooks(InputStream in){
List<Book> books=new ArrayList<Book>();
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
try {
DocumentBuilder builder=factory.newDocumentBuilder();
Document document=builder.parse(in);
Element root=document.getDocumentElement();
NodeList items=root.getElementsByTagName("book");//得到所有的book元素结点,这里也通过元素标签的name得到所有name、price元素结点
for(int i=0;i<items.getLength();i++){
Book book=new Book();
Element bookNode=(Element) items.item(i);//得到每一个book元素结点
book.setId(Integer.parseInt(bookNode.getAttribute("id")));//通过name得到id属性内容
book.setAdd(bookNode.getAttribute("add"));//通过name得到add属性内容
NodeList childNodes=bookNode.getChildNodes();//得到每个book元素结点的子节点,包括name、price元素结点以及这两个元素结点之间的空白TextNode
for(int j=0;j<childNodes.getLength();j++){//遍历每一个子节点
Node node=childNodes.item(j);
if(node.getNodeType()==Node.ELEMENT_NODE){//当子节点是元素结点,而不是空白的TextNode时遍历
Element n=(Element) node;
if("name".equals(n.getNodeName())){
book.setName(n.getFirstChild().getNodeValue());
}
if("price".equals(n.getNodeName())){
book.setPrice(Float.parseFloat(n.getFirstChild().getNodeValue()));
}
}
}
books.add(book);
}
} catch (Exception e) {
return null;
}
return books;
}
}

使用DOM方式解析XML文件,首先找到要解析的标签元素,然后使用getChildNodes()一层一层的向内解析。


(三)、使用Android自带的PULL解析器解析XML文件和写XML文件

1、使用PULL解析器解析XML文件:

PULL方式和SAX方式类似,都提供了判断开始和结束元素标签,使用parser.next()可以进入下一个元素并触发响应的事件,事件作为整数值发送给处理方法,可以通过switch判断开始和结束标签,并通过parser.nextText()方法取得TextNode内容。

public static List<Book> getBooks(InputStream in){
XmlPullParser pullParser=Xml.newPullParser();
try {
pullParser.setInput(in, "utf-8");
int eventType=pullParser.getEventType();
Book book=null;
List<Book> books=null;
while(eventType!=XmlPullParser.END_DOCUMENT){
switch (eventType) {
case XmlPullParser.START_DOCUMENT://开始解析文档
books=new ArrayList<Book>();
break;
case XmlPullParser.START_TAG://开始解析元素标签
String name=pullParser.getName();
if(name.equalsIgnoreCase("book")){
book=new Book();
book.setId(Integer.parseInt(pullParser.getAttributeValue(null, "id")));
book.setAdd(pullParser.getAttributeValue(null, "add"));
}
if(book!=null){
if(name.equalsIgnoreCase("name")){
book.setName(pullParser.nextText());//取得TextNode的内容
}
if(name.equalsIgnoreCase("price")){
book.setPrice(Float.parseFloat(pullParser.nextText()));
}
}
break;
case XmlPullParser.END_TAG://结束解析元素标签
if(pullParser.getName().equalsIgnoreCase("book") && book!=null){
books.add(book);
book=null;
}
break;
}
eventType=pullParser.next();
}
in.close();
return books;
} catch (Exception e) {
return null;
}
}

2、使用PULL方式生成XML文件

/**
* 写入XML文件
* @param books
* @param writer
* @return
*/
public static String writeXML(List<Book> books,Writer writer){
XmlSerializer xmlSerializer=Xml.newSerializer();
try {
xmlSerializer.setOutput(writer);
xmlSerializer.startDocument("UTF-8", true);
xmlSerializer.startTag(null, "books");

for(Book book:books){
xmlSerializer.startTag(null, "book");
xmlSerializer.attribute(null, "id", book.getId()+"");
xmlSerializer.attribute(null, "add", book.getAdd());
xmlSerializer.startTag(null, "name");
xmlSerializer.text(book.getName());
xmlSerializer.endTag(null, "name");
xmlSerializer.startTag(null, "price");
xmlSerializer.text(book.getPrice()+"");
xmlSerializer.endTag(null, "price");
}

xmlSerializer.endTag(null, "books");
xmlSerializer.endDocument();
return writer.toString();
} catch (Exception e) {
return null;
}
}

使用上面的方法:

        File xmlFile=new File("myBook.xml");
FileOutputStream out=new FileOutputStream(xmlFile);
OutputStreamWriter writer=new OutputStreamWriter(out,"UTF-8");
BufferedWriter bw=new BufferedWriter(writer);

String content=PULLParseService.writeXML(books, bw);
bw.flush();
bw.close();
如果不需要得到生成的文件内容,而不需要具体生成文件,可以使用下面的方法:

StringWriter sw=new StringWriter();
PULLParseService.writeXML(books, sw);
String content=sw.toString();

(四)、SAX和PULL方式的区别

SAX解析器的工作方式是自动将事件推入事件处理器进行处理,因此你不能控制事件的处理主动结束;而Pull解析器的工作方式为允许你的应用程序代码主动从解析器中获取事件,正因为是主动获取事件,因此可以在满足了需要的条件后不再获取事件,结束解析。
任意找一个pull的例子和sax的例子比较发现,pull是一个while循环,随时可以跳出,而sax不是,sax是只要解析了,就必须解析完成。


二、对Json数据进行操作

Json是一种比XML更加轻量级的数据格式,有便捷丰富的解析方式。

Json中重要的类:

JSONObject:表示一个Json数据单元;

JSONStringer:可以用来写Json数据,下面写Json数据时会用到;

JSONArray:表示一个数组,每个数组元素是一个JSONObject、JSONArray、int、String、boolean等;

(一)、写Json数据

        /**
* 要写的Json数据为:
* {
* "phone":["18956276082","18956276081"],//数组
* "name":"vampire",//String
* "age":55,//int
* "address":{"country":"china","province","hebei"},//JSONObject
* "married":false//boolean
* }
*/
try {
JSONObject person=new JSONObject();//相当于初始化了{}

JSONArray phone=new JSONArray();//相当于初始化了[]
phone.put("18956276082").put("18956276081");//相当于加入了["18956276082","18956276081"]
person.put("phone", phone);//相当于加入了{"phone":["18956276082","18956276081"]}

person.put("name", "vampire");//相当于加入了{"phone":["18956276082","18956276081"],"name":"vampire"}

person.put("age", 55);

JSONObject address=new JSONObject();//相当于初始化了{}
address.put("country", "china");//相当于加入了{"country":"china"}
address.put("provice", "hebei");
person.put("address", address);

person.put("married", false);
} catch (JSONException e) {
throw new RuntimeException(e);
}

也可以使用JSONStringer来构建Json文本

        /**
* 要写的Json数据为:
* {
* "phone":["18956276082","18956276081"],//数组
* "name":"vampire",//String
* "age":55,//int
* "address":{"country":"china","province","hebei"},//JSONObject
* "married":false//boolean
* }
*/
//使用JSONStringer构建Json文本
try {
JSONStringer js=new JSONStringer();
//object()表示一个JSONObject对象的开始,object()和endObject()必须配对使用
js.object();
js.key("phone");

js.array();
js.value("18956276082").value("18956276081");
js.endArray();

js.key("name");
js.value("vampire");

js.key("age");
js.value(55);

js.key("address");
js.object();
js.key("country");
js.value("china");
js.key("province");
js.value("hebei");
js.endObject();

js.key("married");
js.value(false);
js.endObject();//结束对象
} catch (JSONException e) {
throw new RuntimeException(e);
}

(二)、解析Json数据

/**
* 要解析的Json数据:
* 简单数据{'singer':{'id':01,'name':'tom','gender':'男'}}
* 复杂数据{"singers":[
* {"singer":{'id':02,'name':'tom','gender':'男'}},
* {"singer":{'id':03,'name':'jerry,'gender':'男'}},
* {"singer":{'id':04,'name':'jim,'gender':'男'}},
* {"singer":{'id':05,'name':'lily,'gender':'女'}}
* ]
* }
*/
//解析简单的Json数据
public void parseJson(String json){
try {
JSONObject jsonObject=new JSONObject(json).getJSONObject("singer");
int id=jsonObject.getInt("id");
String name=jsonObject.getString("name");
String gender=jsonObject.getString("gender");
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
//解析复杂的Json数据
public void parseJsonMulti(String json){
try {
JSONArray jo=new JSONObject(json).getJSONArray("singers");
for(int i=0;i<jo.length();i++){
JSONObject jsonObject=jo.optJSONObject(i).getJSONObject("singer");
int id=jsonObject.getInt("id");
String name=jsonObject.getString("name");
String gender=jsonObject.getString("gender");
}
} catch (JSONException e) {
throw new RuntimeException(e);
}
}

(三)、关于使用getType和optType

getType可以将要获取的键的值转化为指定的类型,如果无法转化或者没有值则抛出异常JSONException。

optType也是将要获取的键的值转化为指定的类型,如果无法转化或者没有值则返回指定的默认值。

        person.getLong("name"); // 如果名字没有值或者无法转换为long ,会抛异常
person.optLong("name",1000); // 如果名字没有值或者无法转换为long ,会返回1000