Android中XML文件的解析

AndroidXML文件的解析

我们知道XML格式的数据在很多软件开发中都会遇到,那么在Android开发中也不例外。那么接下来要如何去解析和创建XML文件那?下面就和大家一起研究和学习关于XML文件的解析方式和如何创建XML文件。

Android中有三种方式可以解析XML文件,分别为PULL解析方式、SAX解析方式和DOM解析方式,那么下面就来看看这几种解析方式之间的联系和特点。

PULL解析方式:PULL解析器方式是采用事件处理的模式,主要是围绕着事件源和事件处理器工作的。一旦事件源被触发之后,事件源会调用事件处理方法进行相应的处理操作。我们只需要在事件处理方法中添加我们的处理事件逻辑即可。

特点:小巧易用、解析速度快,占用内存也较少,非常适合应用在Android移动设别中,Android系统内部在解析各种XML的时候也是采用这种方式进行解析的。

SAX解析方式:SAX解析方式和PULL解析方式有些相同,也就是他们都是基于事件的处理模式,不同的是SAX解析方式在事件源触发之后,会自动调用相应的处理器方法来处理解析逻辑,而PULL则需要自己编写事件处理方法。

特点:解析速度快、占用内存也较少,适合在Android移动设备中使用,但使用起来没有PULL那样小巧轻便。

DOM解析方式:DOM解析方式与上面的两种方式截然不同,它是采用树状结构来封装数据信息,我们可以使用DOM API来遍历XML树,一般情况下,如果想遍历和更新XML树的话,我们需要将XML文档整个文档或构造的结构加载到内存,才能进行需要的遍历和更新。

特点:解析速度很快(由于在内存中以XML树结构存储数据,遍历速度很快),但如果XML文件较大的时候,解析和加载这个大文档会耗费很多系统资源的。

通过上面,我们知道了几种在Android中可以解析XML文档的方式以及他们各自的优缺点,所以读者可以根据需要折中选择。当然,我个人习惯使用PULL解析方式来解析XML,那么接下来就先介绍使用PULL解析器来解析XML文档。

新建一个Android项目,结构目录如下:

如上图所示,我们首先需要创建一个Person类和一个XML文件person.xml并放在了assets资源目录中,一般实际开发中,XML文档来自于网络,这里是为了验证XML文档的解析。

代码分别如下:

Person.java:

public class Person {

private String name;

private int age;

private String tel;

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public int getAge() {

return age;

}

public void setAge(int age) {

this.age = age;

}

public String getTel() {

return tel;

}

public void setTel(String tel) {

this.tel = tel;

}

}

Person.xml:

<?xml version="1.0" encoding="utf-8"?>

<persons>

    <person>

        <name>David</name>

        <age>27</age>

        <tel>1500000000</tel>

    </person>

    <person>

        <name>why</name>

        <age>28</age>

        <tel>1500000001</tel>

    </person>

    <person>

        <name>jake</name>

        <age>29</age>

        <tel>1500000002</tel>

    </person>

    <person>

        <name>Make</name>

        <age>30</age>

        <tel>1500000003</tel>

    </person>

</persons>

接下来,我们需要创建一个规范的解析接口PersonParser.java,代码如下:

public interface PersonParser {

// 解析XML接口方法

public List<Person> parser(InputStream is) throws Exception;

// 生成XML序列化方法

public String serializer(List<Person> persons) throws Exception;

}

这个接口的作用主要是规范化三种解析方式的通用性,每种解析方式都需要实现该接口。

PULL解析方式:

public class PullPersonParser implements PersonParser{

@Override

public List<Person> parser(InputStream is) throws Exception {

List<Person> persons = null;

Person person = null;

// 实例化一个解析器XmlPullParser

XmlPullParser parser = Xml.newPullParser();

// 设置输入流并指定编码格式

parser.setInput(is, "UTF-8");

// 基于事件模式的处理 

int eventType = parser.getEventType();

while(eventType != XmlPullParser.END_DOCUMENT) {

switch(eventType) {

case XmlPullParser.START_DOCUMENT: {

persons = new ArrayList<Person>();

}

case XmlPullParser.START_TAG: {

if(parser.getName().equals("person")) {

person = new Person();

}

else if(parser.getName().equals("name")) {

eventType = parser.next();

person.setName(parser.getText());

}

else if(parser.getName().equals("age")) {

eventType = parser.next();

person.setAge(Integer.parseInt(parser.getText()));

}

else if(parser.getName().equals("tel")) {

eventType = parser.next();

person.setTel(parser.getText());

}

}

case XmlPullParser.END_TAG: {

if(parser.getName().equals("person")) {

persons.add(person);

person = null;

}

break;

}

}

eventType = parser.next();

}

return persons;

}

@Override

public String serializer(List<Person> persons) throws Exception {

// 实例化一个XmlSerializer

XmlSerializer serializer = Xml.newSerializer(); 

StringWriter writer = new StringWriter();

// 设置输出为writer

serializer.setOutput(writer);

serializer.startDocument("UTF-8"true);

serializer.startTag("p""persons");

for(Person p:persons) {

serializer.startTag("p""person");

serializer.attribute("p""name",p.getName());

serializer.startTag("p","age");

serializer.text(p.getAge() + "");

serializer.endTag("p""age");

serializer.startTag("p""tel");

serializer.text(p.getTel());

serializer.endTag("p""tel");

serializer.endTag("p""person");

}

serializer.endTag("p""persons");

serializer.endDocument();

return writer.toString();

}

}

最后,我们在XMLParserAct.java中添加两个按钮,分别为readerwriter,用来解析和生成XML文档,解析出的数据,使用Log将其打印在控制台,而生成的XML文档默认存放在程序的安装目录files中。具体实现如下:

public class XMLParserAct extends Activity {

public static String TAG = "XMLParserAct";

private Button reader;

private Button writer;

PullPersonParser pullParser = new PullPersonParser();

static List<Person> persons = new ArrayList<Person>();

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

initView();

}

private void initView() {

reader = (Button) findViewById(R.id.reader);

writer = (Button) findViewById(R.id.writer);

OnClickListener c = new OnClickListener() {

public void onClick(View v) {

if(v == reader) {// 解析

parserPerson();

}

else if(v == writer) {// 生成

newPerson();

}

}

};

reader.setOnClickListener(c);

writer.setOnClickListener(c);

}

private void parserPerson() {

try {

InputStream is = getAssets().open("person.xml");

persons = pullParser.parser(is);

for(Person p:persons) {

Log.i("TAG",p.toString());

}

catch (IOException e) {

e.printStackTrace();

catch (Exception e) {

e.printStackTrace();

}

}

private void newPerson() {

try {

// 序列化 生成XML文档

String xml = pullParser.serializer(persons);

FileOutputStream fos = openFileOutput("person.xml",Context.MODE_PRIVATE);

fos.write(xml.getBytes("UTF-8"));

catch (Exception e) {

e.printStackTrace();

}

}

}

运行效果图---reader:

运行效果图---writer:

同时,新生成的XML文档的格式与原来的XML格式略不同,当不影响我们对其的解析和生成操作,如下所示:

<?xml version="1.0" encoding="utf-8"?>

<persons>

    <person name=David>

        <age>27</age>

        <tel>1500000000</tel>

    </person>

    <person name=why>

        <age>28</age>

        <tel>1500000001</tel>

    </person>

    <person name=jake>

        <age>29</age>

        <tel>1500000002</tel>

    </person>

<person name=Make>

    <age>30</age>

        <tel>1500000003</tel>

    </person>

</persons>

 

SAX解析器解析XML文档:

对于使用SAX解析方式解析XML文档的操作与PULL方式同样基于事件处理模式进行的。由于大部分准备工作相同,这里主要介绍不同的部分SAXPersonParser和稍微改动的Activity部分。具体如下:

SAXPersonParser代码如下:

public class SaxPersonParser implements PersonParser{

@Override

public List<Person> parser(InputStream is) throws Exception {

// 创建一个SAXParserFactory工厂实例

SAXParserFactory factory = SAXParserFactory.newInstance();

// 从工厂factory中获得具体解析实例

SAXParser parser = factory.newSAXParser();

// 自定义规则解析输入流

CustomHandler cusHandler = new CustomHandler();

// 按照自定的解析逻辑进行解析

parser.parse(is, cusHandler);

return cusHandler.getPersons();

}

@Override

public String serializer(List<Person> persons) throws Exception {

// 创建一个SAXTransformerFactory序列化工厂实例

SAXTransformerFactory factory = (SAXTransformerFactory)TransformerFactory.newInstance();

// 从factory中获得具体的序列化处理实例 

TransformerHandler handler = factory.newTransformerHandler();

// 从handler中获取实际序列化实例

Transformer transFormer = handler.getTransformer();

// 设置输出的编码格式

transFormer.setOutputProperty(OutputKeys.ENCODING,"UTF-8");

// 设置是否忽略XML声明

transFormer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION"no");

// 设置是否自动添加额外空白

transFormer.setOutputProperty(OutputKeys.INDENT"yes");

// 解析逻辑的解析结果添加到序列流中

StringWriter writer = new StringWriter();

Result result = new StreamResult(writer);

handler.setResult(result);

// 代表命名空间的URI 当URI无值时 须置为空字符串  

String uri = "";    

//命名空间的本地名称(不包含前缀) 当没有进行命名空间处理时 须置为空字符串  

String localName = "";  

// 解析

handler.startDocument();  

handler.startElement(uri, localName, "books"null);  

// 负责存放元素的属性信息      

AttributesImpl attrs = new AttributesImpl();    

char[] ch = null;  

for (Person person : persons) {  

attrs.clear();  // 清空属性列表  

// 添加一个名为name的属性(type影响不大,这里设为string)  

attrs.addAttribute(uri, localName, "name""string", String.valueOf(person.getName()));

// 开始一个person元素 关联上面设定的name属性  

handler.startElement(uri, localName, "person", attrs);    

// 开始一个age元素 没有属性  

handler.startElement(uri, localName, "age"null);

ch = String.valueOf(person.getAge()).toCharArray(); 

// 设置age元素的文本节点  

handler.characters(ch, 0, ch.length);   

handler.endElement(uri, localName, "age");  

// 开始一个tel元素 没有属性  

handler.startElement(uri, localName, "tel"null);

ch = String.valueOf(person.getTel()).toCharArray(); 

// 设置age元素的文本节点  

handler.characters(ch, 0, ch.length);   

handler.endElement(uri, localName, "tel");  

handler.endElement(uri, localName, "person");  

}  

handler.endElement(uri, localName, "persons");  

handler.endDocument();  

return writer.toString();  

}

private class CustomHandler extends DefaultHandler {

private List<Person> persons = null;

private Person person = null;

private StringBuilder builder = null;

@Override

public void startDocument() throws SAXException {

super.startDocument();

persons = new ArrayList<Person>();

builder = new StringBuilder();

}

@Override

public void startElement(String uri, String localName, String qName,

Attributes attributes) throws SAXException {

super.startElement(uri, localName, qName, attributes);

if(localName.equals("person")) {

person = new Person();

}

// 重新设置StringBuilder的字符长度为0,以便重新开始读取字符节点信息

builder.setLength(0);

}

@Override

public void characters(char[] ch, int start, int length)

throws SAXException {

super.characters(ch, start, length);

// 将读取的字符添加到StringBuilder中

builder.append(ch, start, length);

}

@Override

public void endElement(String uri, String localName, String qName)

throws SAXException {

super.endElement(uri, localName, qName);

if(localName.equals("name")) {

person.setName(builder.toString());

}

else if(localName.equals("age")) {

person.setAge(Integer.parseInt(builder.toString()));

}

else if(localName.equals("tel")) {

person.setTel(builder.toString());

}

else if(localName.equals("person")) {

persons.add(person);

}

}

// 返回解析后得到的数据

public List<Person> getPersons() {

return persons;

}

}

activity部分,只需要稍作改动即可,如下所示:

private void parserPerson() {

try {

InputStream is = getAssets().open("person.xml");

//pullParser = new PullPersonParser();

//persons = pullParser.parser(is);

saxParser = new SaxPersonParser();  

persons = saxParser.parser(is);

for(Person p:persons) {

Log.i("TAG",p.toString());

}

catch (IOException e) {

e.printStackTrace();

catch (Exception e) {

e.printStackTrace();

}

}

private void newPerson() {

try {

// 序列化 生成XML文档

//String xml = pullParser.serializer(persons);

String xml = saxParser.serializer(persons);

FileOutputStream fos = openFileOutput("person.xml",Context.MODE_PRIVATE);

fos.write(xml.getBytes("UTF-8"));

catch (Exception e) {

e.printStackTrace();

}

}

注意:

代码中,我们定义了自己的事件处理逻辑,重写了DefaultHandler的几个重要的事件方法。DefaultHandler是一个事件处理器,可以接收解析器报告的所有事件,处理所发现的数据。它实现了EntityResolver接口、DTDHandler接口、ErrorHandler接口和ContentHandler接口。这几个接口代表不同类型的事件处理器。我们着重介绍一下ContentHandler接口。结构如图:

这几个比较重要的方法已被我用红线标注,DefaultHandler实现了这些方法,但在方法体内没有做任何事情,因此我们在使用时必须覆写相关的方法。

最重要的是startElement方法、characters方法和endElement方法。当执行文档时遇到起始节点,startElement方法将会被调用,我们可以获取起始节点相关信息;

然后characters方法被调用,我们可以获取节点内的文本信息;

最后endElement方法被调用,我们可以做收尾的相关操作。

上面使用的SAX方式的解析结果和生成XML文档的结果与PULL解析方式是一样的,具体的效果图不在这里罗列。

DOM解析方式解析XML:

DomPersonParser.java:

 public class DomBookParser implements BookParser { 

 

 @Override 

 public List<Book> parse(InputStream is) throws Exception { 

 List<Book> books = new ArrayList<Book>(); 

 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); //取得DocumentBuilderFactory实例 

 DocumentBuilder builder = factory.newDocumentBuilder(); //factory获取DocumentBuilder实例 

 Document doc = builder.parse(is); //解析输入流 得到Document实例 

 Element rootElement = doc.getDocumentElement(); 

 NodeList items = rootElement.getElementsByTagName("book"); 

 for (int i = 0; i < items.getLength(); i++) { 

 Book book = new Book(); 

 Node item = items.item(i); 

 NodeList properties = item.getChildNodes(); 

 for (int j = 0; j < properties.getLength(); j++) { 

 Node property = properties.item(j); 

 String nodeName = property.getNodeName(); 

 if (nodeName.equals("id")) { 

 book.setId(Integer.parseInt(property.getFirstChild().getNodeValue())); 

 else if (nodeName.equals("name")) { 

 book.setName(property.getFirstChild().getNodeValue()); 

 else if (nodeName.equals("price")) { 

 book.setPrice(Float.parseFloat(property.getFirstChild().getNodeValue())); 

 

 

 books.add(book); 

 

 return books; 

 

 

 @Override 

 public String serialize(List<Book> books) throws Exception { 

 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 

 DocumentBuilder builder = factory.newDocumentBuilder(); 

 Document doc = builder.newDocument(); //builder创建新文档 

 

 Element rootElement = doc.createElement("books"); 

 

 for (Book book : books) { 

 Element bookElement = doc.createElement("book"); 

 bookElement.setAttribute("id", book.getId() + ""); 

 

 Element nameElement = doc.createElement("name"); 

 nameElement.setTextContent(book.getName()); 

 bookElement.appendChild(nameElement); 

 

 Element priceElement = doc.createElement("price"); 

 priceElement.setTextContent(book.getPrice() + ""); 

 bookElement.appendChild(priceElement); 

 

 rootElement.appendChild(bookElement); 

 

 

 doc.appendChild(rootElement); 

 

 TransformerFactory transFactory = TransformerFactory.newInstance();//取得TransformerFactory实例 

 Transformer transformer = transFactory.newTransformer(); //transFactory获取Transformer实例 

 transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); // 设置输出采用的编码方式 

 transformer.setOutputProperty(OutputKeys.INDENT, "yes"); // 是否自动添加额外的空白 

 transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); // 是否忽略XML声明 

 

 StringWriter writer = new StringWriter(); 

 

 Source source = new DOMSource(doc); //表明文档来源是doc 

 Result result = new StreamResult(writer);//表明目标结果为writer 

 transformer.transform(source, result); //开始转换 

 

 return writer.toString(); 

 

 

 

activity部分,只需要稍作改动即可,如下所示:

private void parserPerson() {

try {

InputStream is = getAssets().open("person.xml");

//pullParser = new PullPersonParser();

//persons = pullParser.parser(is);

//saxParser = new SaxPersonParser();  

//persons = saxParser.parser(is);

domParser = new DomPersonParser();  

persons = domParser .parser(is);

for(Person p:persons) {

Log.i("TAG",p.toString());

}

catch (IOException e) {

e.printStackTrace();

catch (Exception e) {

e.printStackTrace();

}

}

解析和创建XML的效果和上面的两种方式相同。

 

 

技术交流群:179914858

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云水之路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值