XML介绍 XML(Extensible Markup Language) 即可扩展标记语言,与HTML一样,都是SGML(Standard Generalized Markup Language,标准通用标记语言)。XML是跨平台的,依赖于内容的技术,是当前处理结构化文档信息的有利工具。扩展标记语言XML是一种简单的数据存储语言,使用一系列简单的标记描述数据,而这些标记可以用方便的方式建立,虽然XML占用的空间比二进制数据要占用更多的空间,但XML简单且易于使用。 由于XML扩展性强,致使它需要稳定的基础规则来支持扩展。XML语言规则:


  • 起始和结束的标签相匹配
  • 嵌套标签不能相互嵌套
  • 区分大小写

Android SAX、DOM、Pull解析xml文件剖析与案例讲解_xml文件


Android SAX、DOM、Pull解析xml文件剖析与案例讲解_xml文件_02


一、使用SAX解析XML SAX使用流式处理的方式,当遇到一个标签时,它并不会记录下以前所碰到的标签,也就是说,在startElement()方法中,你所知道的信息,仅仅是当前标签的名字和属性,至于标签的嵌套结构,上层标签的名字,是否有子元素等其他与结构相关的信息,都是不知道的,都需要通过编码来完成。 SAX进行XML解析的整个过程: SAXParserHandler: ***startDocument()*** SAXParserHandler: startElement uri: localName:persons qName:persons SAXParserHandler: persons***startElement()*** SAXParserHandler: startElement uri: localName:person qName:person SAXParserHandler: attributeName:id attributeValue:23 SAXParserHandler: person***startElement()*** SAXParserHandler: startElement uri: localName:name qName:name SAXParserHandler: name***startElement()*** SAXParserHandler: content: zhangsan SAXParserHandler: name***endElement()*** SAXParserHandler: startElement uri: localName:age qName:age SAXParserHandler: age***startElement()*** SAXParserHandler: content: 21 SAXParserHandler: age***endElement()*** SAXParserHandler: person***endElement()*** SAXParserHandler: startElement uri: localName:person qName:person SAXParserHandler: attributeName:id attributeValue:20 SAXParserHandler: person***startElement()*** SAXParserHandler: startElement uri: localName:name qName:name SAXParserHandler: name***startElement()*** SAXParserHandler: content: wagnwu SAXParserHandler: name***endElement()*** SAXParserHandler: startElement uri: localName:age qName:age SAXParserHandler: age***startElement()*** SAXParserHandler: content: 25 SAXParserHandler: age***endElement()*** SAXParserHandler: person***endElement()*** SAXParserHandler: persons***endElement()*** SAXParserHandler: ***endDocument()*** 从上面Log打印的信息可以了解到SAX解析XML的整个过程和顺序。 为简化工作,SAX为DefaultHandler类提供了接口的默认实现,在大多数情况下,为应用程序扩展DefaultHandler并覆盖相关的方法要比直接实现一个接口更容易。 SAX解析xml案例: 1. 创建Android XMLParser工程 2. 创建SAXParserHandler类: 复写方法:startDocument、startElement、characters、endElement、endDocument 3.Person实体类 package com.xml.demo; public class Person { private Integer id; private String name; private Short age; public Person() { } public Person(Integer id, String name, Short age) { this.id = id; this.name = name; this.age = age; } public Person(String name, Short age) { this.name = name; this.age = age; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Short getAge() { return age; } public void setAge(Short age) { this.age = age; } @Override public String toString() { return "id=" + id + ",name=" + name + ",age=" + age; } } 4 .assets\test.xml <?xml version="1.0" encoding="UTF-8"?> <persons> <person id = "23"> <name>zhangsan</name> <age>21</age> </person> <person id = "20"> <name>wangwu</name> <age>25</age> </person> </persons> 5. SAXParserHandler类: package com.xml.demo.sax; 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; import com.xml.demo.Person; public class SAXParserHandler extends DefaultHandler { private static final String TAG = "SAXParserHandler"; private List<Person> persons; private String perTag;// 通过此变量,记录前一个标签的名称。 Person person;// 记录当前Person public List<Person> getPersons() { return persons; } // 该函数只在开始解析文档时执行一次,比较适合处理一些初始化的行为 public void startDocument() throws SAXException { persons = new ArrayList<Person>(); Log.i(TAG, "***startDocument()***"); } public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { Log.i(TAG, "startElement uri:" + uri + " localName:" + localName + " qName:" + qName); if ("person".equals(localName)) { for (int i = 0; i < attributes.getLength(); i++) { Log.i(TAG, "attributeName:" + attributes.getLocalName(i) + " attributeValue:" + attributes.getValue(i)); person = new Person(); person.setId(Integer.valueOf(attributes.getValue(i))); } } perTag = localName; Log.i(TAG, qName + "***startElement()***"); } public void characters(char[] ch, int start, int length) throws SAXException { String data = new String(ch, start, length).trim(); if (!"".equals(data.trim())) { Log.i(TAG, "content: " + data.trim()); } if ("name".equals(perTag)) { person.setName(data); } else if ("age".equals(perTag)) { person.setAge(new Short(data)); } } public void endElement(String uri, String localName, String qName) throws SAXException { Log.i(TAG, qName + "***endElement()***"); if ("person".equals(localName)) { persons.add(person); person = null; } perTag = null; } public void endDocument() throws SAXException { Log.i(TAG, "***endDocument()***"); } } 6.MainActivity类: package com.xml.demo; import java.io.IOException; import java.io.InputStream; import java.util.List; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.SAXException; import android.app.Activity; import android.content.res.AssetManager; import android.os.Bundle; import android.util.Log; import com.xml.demo.sax.SAXParserHandler; public class MainActivity extends Activity { String TAG = "ZZMainActivity"; private AssetManager mAssetManager; private InputStream mInputStream; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mAssetManager = getApplicationContext().getAssets(); try { mInputStream = mAssetManager.open("test.xml"); } catch (IOException e) { e.printStackTrace(); } testSAXParser(); } private void testSAXParser() { SAXParserHandler saxForHandler = new SAXParserHandler(); SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser saxParser = null; try { saxParser = spf.newSAXParser(); saxParser.parse(mInputStream, saxForHandler); List<Person> persons = saxForHandler.getPersons(); for (Person person : persons) { Log.i(TAG, person.toString()); } } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } SAX以流的方式接收指定的XML内容。通过上面SAX解析过程,要清楚,遇到哪些字符,会触发哪些事件,触发的顺序又是怎么样。 二、使用DOM解析XML(不推荐使用) DOX 是一种用于XML文档的对象模型,可用于直接访问XML文档的各个部分。没有涉及回调和复杂的状态管理,然后,DOM实现常常将所有XML节点保存到内存中,这使较大的文档效率低下。 通过DOM将XML文档作为一个树形结构,这种树形街结构也被称为节点树 Android SAX、DOM、Pull解析xml文件剖析与案例讲解_xml_03


DOM是这样规定的:


  • 整个文档是一个文档节点;
  • 每个XML元素标签是一个元素节点;
  • 包含在XML元素中的文本是文本节点;
  • 每一个XML属性是一个属性节点;
  • 注释属于注释节点。

节点树中的节点之间彼此都有等级关系。 在节点树中,顶端的节点成为根节点; 根节点之外的每个节点都有一个父节点; 节点可以有任何数量的子节点; 叶子是没有子节点的节点; 同级节点是拥有相同父节点的节点。 示例: 1. DOMParserHandler package com.xml.demo.dom; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.xml.demo.Person; public class DOMParserHandler { private static final String TAG = "DOMParserHandler"; public static List<Person> getPersons(InputStream inStream) throws Exception { List<Person> persons = new ArrayList<Person>(); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.parse(inStream); Element root = document.getDocumentElement(); NodeList personNodes = root.getElementsByTagName("person"); for (int i = 0; i < personNodes.getLength(); i++) { Element personElement = (Element) personNodes.item(i); int id = Integer.valueOf(personElement.getAttribute("id")); Person person = new Person(); person.setId(id); NodeList childNodes = personElement.getChildNodes(); for (int y = 0; y < childNodes.getLength(); y++) { if (childNodes.item(y).getNodeType() == Node.ELEMENT_NODE) { if ("name".equals(childNodes.item(y).getNodeName())) { String name = childNodes.item(y).getFirstChild().getNodeValue(); person.setName(name); } else if ("age".equals(childNodes.item(y).getNodeName())) { String age = childNodes.item(y).getFirstChild().getNodeValue(); person.setAge(new Short(age)); } } } persons.add(person); } inStream.close(); return persons; } } 2. package com.xml.demo; import java.io.IOException; import java.io.InputStream; import java.util.List; import android.app.Activity; import android.content.res.AssetManager; import android.os.Bundle; import android.util.Log; import com.xml.demo.dom.DOMParserHandler; public class MainActivity extends Activity { String TAG = "ZZMainActivity"; private AssetManager mAssetManager; private InputStream mInputStream; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mAssetManager = getApplicationContext().getAssets(); try { mInputStream = mAssetManager.open("test.xml"); } catch (IOException e) { e.printStackTrace(); } testDOMParser(); } private void testDOMParser() { try { List<Person> persons = DOMParserHandler.getPersons(mInputStream); for (Person person : persons) { Log.i(TAG, person.toString()); } } catch (Exception e) { e.printStackTrace(); } } } 三、使用PULL解析XML(推荐使用这种方式) PULL是Android系统内置的解析器,但不仅限于Android使用 Pull解析器的运行方式和SAX解析器相似。它提供了类似的事件,如开始元素和结束元素事件。使用parser.next()可以进入下一个元素并触发相应事件。事件将作为数值代码被发送,因此可以使用一个switch对感兴趣的事件进行选择,然后进行相应处理。当元素开始解析时,调用parser.nextText()方法可以获取下一个Text类型元素的值。 Pull解析器只有一个重要的方法next();它被用来检索下一个事件。而它的事件也仅仅只有5个:


  • START_DOCUMENT(开始解析)
  • START_TAG(开始元素)
  • TEXT(解析文本)
  • END_TAG(结束元素)
  • END_DOCUMENT(结束解析)

解析XML内容的方式与SAX是相似的,同样包括开始元素和结束元素事件,使用parser.next()可以进入下一个元素并触发相应事件。但是他们不同的是,SAX的事件驱动是回调相应方法,需要提供回调的方法,而后在SAX内部自动调用相应的方法。而pull解析器并没有强制要求提供触发的方法。因为它触发的事件并不是一个方法,而是一个数字。至于触发的事件要不要处理,由程序员自己决定。 示例: 1. PullParserHandler package com.xml.demo.pull; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import org.xmlpull.v1.XmlPullParser; import android.util.Xml; import com.xml.demo.Person; public class PullParserHandler { private static final String TAG = "PullParserHandler"; public static List<Person> getPersons(InputStream inStream) throws Exception { Person person = null; List<Person> persons = null; XmlPullParser pullParser = Xml.newPullParser(); pullParser.setInput(inStream, "UTF-8"); int event = pullParser.getEventType();// 触发第一个事件 while (event != XmlPullParser.END_DOCUMENT) { switch (event) { case XmlPullParser.START_DOCUMENT: persons = new ArrayList<Person>(); break; case XmlPullParser.START_TAG: if ("person".equals(pullParser.getName())) { int id = Integer.valueOf(pullParser.getAttributeValue(0)); person = new Person(); person.setId(id); } if (person != null) { if ("name".equals(pullParser.getName())) { person.setName(pullParser.nextText()); } if ("age".equals(pullParser.getName())) { person.setAge(new Short(pullParser.nextText())); } } break; case XmlPullParser.END_TAG: if ("person".equals(pullParser.getName())) { persons.add(person); person = null; } break; } event = pullParser.next(); } return persons; } } 说明: a. “int event = pullParser.getEventType()”是pull解析器的第一个事件,这个方法的返回值是int类型,这就是前面提到的pull解析器返回的是一个数字,类似于一个信号。那么这些信号都代表什么意思呢?Pull解析器已经定义了这五个常亮,而且对于事件,仅仅只有这5个,如下:


  1. START_DOCUMENT(开始解析)
  2. START_TAG(开始元素)
  3. TEXT(解析文本)
  4. END_TAG(结束元素)
  5. END_DOCUMENT(结束解析)

b. pullParser.getEventType()触发了第一个事件,根据XML的语法,也就是从它开始了解析文档。那么,怎么样触发下一个事件呢?要通过parser中最重要的方法: pullParser.next() 注意:该方法是有返回值的,在Pull触发下一个事件的同时,也获得该事件的“信号”。通过获得的信号进行switch操作。 c. pullParser.getAttributeValue获得相应属性的值。它有两种形式,可以通过属性的索引,也可以通过(命名空间,属性名)进行索引。 2. MainActivity package com.xml.demo; import java.io.IOException; import java.io.InputStream; import java.util.List; import android.app.Activity; import android.content.res.AssetManager; import android.os.Bundle; import android.util.Log; import com.xml.demo.pull.PullParserHandler; public class MainActivity extends Activity { String TAG = "ZZMainActivity"; private AssetManager mAssetManager; private InputStream mInputStream; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mAssetManager = getApplicationContext().getAssets(); try { mInputStream = mAssetManager.open("test.xml"); } catch (IOException e) { e.printStackTrace(); } testPullParser(); } private void testPullParser() { List<Person> persons; try { persons = PullParserHandler.getPersons(mInputStream); if (persons != null) { for (Person person : persons) { Log.i(TAG, person.toString()); } } } catch (Exception e) { e.printStackTrace(); } } } 四、使用PULL生成XML SAX不支持xml文件的修改、生成,下面介绍下如何使用Pull来生成一个XML XML中的标签时成对出现的,又开始,必有结束,所以在写入一个startTag()时,笔者习惯紧跟着写入与之相对应的endTag(),这样不至于在复杂的生成结构时,弄错了XML生成结构 示例1: 1. PullParserHandler.java package com.xml.demo.pull; import java.io.OutputStream; import java.util.List; import org.xmlpull.v1.XmlSerializer; import android.util.Xml; import com.xml.demo.Person; public class PullParserHandler { private static final String TAG = "PullParserHandler"; public static void save(List<Person> persons, OutputStream outStream) throws Exception { XmlSerializer serializer = Xml.newSerializer(); serializer.setOutput(outStream, "UTF-8"); serializer.startDocument("UTF-8", true); serializer.startTag(null, "persons"); for (Person person : persons) { serializer.startTag(null, "person"); serializer.attribute(null, "id", person.getId().toString()); serializer.startTag(null, "name"); serializer.text(person.getName()); serializer.endTag(null, "name"); serializer.startTag(null, "age"); serializer.text(person.getAge().toString()); serializer.endTag(null, "age"); serializer.endTag(null, "person"); } serializer.endTag(null, "persons"); serializer.endDocument(); outStream.flush(); outStream.close(); } } 2. MainActivity.java package com.xml.demo; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.content.res.AssetManager; import android.os.Bundle; import com.xml.demo.pull.PullParserHandler; public class MainActivity extends Activity { String TAG = "ZZMainActivity"; private AssetManager mAssetManager; private InputStream mInputStream; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); testSaveXml(); } private void testSaveXml() { File file = new File(this.getFilesDir(), "toXmlFile.xml"); FileOutputStream outStream; try { outStream = new FileOutputStream(file); List<Person> persons = new ArrayList<Person>(); persons.add(new Person(90, "zhangsan", (short) 13)); persons.add(new Person(35, "wangwu", (short) 23)); persons.add(new Person(78, "zhaoliu", (short) 33)); PullParserHandler.save(persons, outStream); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } } 上述生成的xml文件保存在 /data/data/com.xml.demo/files目录下: <?xml version='1.0' encoding='UTF-8' standalone='yes' ?><persons><person id="90"><name>zhangsan</name><age>13</age></person><person id="35"><name>wangwu</name><age>23</age></person><person id="78"><name>zhaoliu</name><age>33</age></person></persons> 示例2: 使用Android提供的工具类:FastXmlSerializer.java 和 XmlUtils.java 来生成xml文件,生成的xml文件是格式化后的,比较容易阅读,推荐使用 1. 工具类:FastXmlSerializer.java package com.xml.demo.utils; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.io.Writer; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; import java.nio.charset.CoderResult; import java.nio.charset.IllegalCharsetNameException; import java.nio.charset.UnsupportedCharsetException; import org.xmlpull.v1.XmlSerializer; // copy from frameworks/base/core/java/com/android/internal/util/FastXmlSerializer.java 1.6 /** * This is a quick and dirty implementation of XmlSerializer that isn't horribly * painfully slow like the normal one. It only does what is needed for the * specific XML files being written with it. */ public class FastXmlSerializer implements XmlSerializer { private static final String ESCAPE_TABLE[] = new String[] { null, null, null, null, null, null, null, null, // 0-7 null, null, null, null, null, null, null, null, // 8-15 null, null, null, null, null, null, null, null, // 16-23 null, null, null, null, null, null, null, null, // 24-31 null, null, "&quot;", null, null, null, "&amp;", null, // 32-39 null, null, null, null, null, null, null, null, // 40-47 null, null, null, null, null, null, null, null, // 48-55 null, null, null, null, "&lt;", null, "&gt;", null, // 56-63 }; private static final int BUFFER_LEN = 8192; private final char[] mText = new char[BUFFER_LEN]; private int mPos; private Writer mWriter; private OutputStream mOutputStream; private CharsetEncoder mCharset; private ByteBuffer mBytes = ByteBuffer.allocate(BUFFER_LEN); private boolean mInTag; private void append(char c) throws IOException { int pos = mPos; if (pos >= (BUFFER_LEN - 1)) { flush(); pos = mPos; } mText[pos] = c; mPos = pos + 1; } private void append(String str, int i, final int length) throws IOException { if (length > BUFFER_LEN) { final int end = i + length; while (i < end) { int next = i + BUFFER_LEN; append(str, i, next < end ? BUFFER_LEN : (end - i)); i = next; } return; } int pos = mPos; if ((pos + length) > BUFFER_LEN) { flush(); pos = mPos; } str.getChars(i, i + length, mText, pos); mPos = pos + length; } private void append(char[] buf, int i, final int length) throws IOException { if (length > BUFFER_LEN) { final int end = i + length; while (i < end) { int next = i + BUFFER_LEN; append(buf, i, next < end ? BUFFER_LEN : (end - i)); i = next; } return; } int pos = mPos; if ((pos + length) > BUFFER_LEN) { flush(); pos = mPos; } System.arraycopy(buf, i, mText, pos, length); mPos = pos + length; } private void append(String str) throws IOException { append(str, 0, str.length()); } private void escapeAndAppendString(final String string) throws IOException { final int N = string.length(); final char NE = (char) ESCAPE_TABLE.length; final String[] escapes = ESCAPE_TABLE; int lastPos = 0; int pos; for (pos = 0; pos < N; pos++) { char c = string.charAt(pos); if (c >= NE) continue; String escape = escapes[c]; if (escape == null) continue; if (lastPos < pos) append(string, lastPos, pos - lastPos); lastPos = pos + 1; append(escape); } if (lastPos < pos) append(string, lastPos, pos - lastPos); } private void escapeAndAppendString(char[] buf, int start, int len) throws IOException { final char NE = (char) ESCAPE_TABLE.length; final String[] escapes = ESCAPE_TABLE; int end = start + len; int lastPos = start; int pos; for (pos = start; pos < end; pos++) { char c = buf[pos]; if (c >= NE) continue; String escape = escapes[c]; if (escape == null) continue; if (lastPos < pos) append(buf, lastPos, pos - lastPos); lastPos = pos + 1; append(escape); } if (lastPos < pos) append(buf, lastPos, pos - lastPos); } public XmlSerializer attribute(String namespace, String name, String value) throws IOException, IllegalArgumentException, IllegalStateException { append(' '); if (namespace != null) { append(namespace); append(':'); } append(name); append("=\""); escapeAndAppendString(value); append('"'); return this; } public void cdsect(String text) throws IOException, IllegalArgumentException, IllegalStateException { throw new UnsupportedOperationException(); } public void comment(String text) throws IOException, IllegalArgumentException, IllegalStateException { throw new UnsupportedOperationException(); } public void docdecl(String text) throws IOException, IllegalArgumentException, IllegalStateException { throw new UnsupportedOperationException(); } public void endDocument() throws IOException, IllegalArgumentException, IllegalStateException { flush(); } public XmlSerializer endTag(String namespace, String name) throws IOException, IllegalArgumentException, IllegalStateException { if (mInTag) { append(" />\n"); } else { append("</"); if (namespace != null) { append(namespace); append(':'); } append(name); append(">\n"); } mInTag = false; return this; } public void entityRef(String text) throws IOException, IllegalArgumentException, IllegalStateException { throw new UnsupportedOperationException(); } private void flushBytes() throws IOException { int position; if ((position = mBytes.position()) > 0) { mBytes.flip(); mOutputStream.write(mBytes.array(), 0, position); mBytes.clear(); } } public void flush() throws IOException { // Log.i("PackageManager", "flush mPos=" + mPos); if (mPos > 0) { if (mOutputStream != null) { CharBuffer charBuffer = CharBuffer.wrap(mText, 0, mPos); CoderResult result = mCharset.encode(charBuffer, mBytes, true); while (true) { if (result.isError()) { throw new IOException(result.toString()); } else if (result.isOverflow()) { flushBytes(); result = mCharset.encode(charBuffer, mBytes, true); continue; } break; } flushBytes(); mOutputStream.flush(); } else { mWriter.write(mText, 0, mPos); mWriter.flush(); } mPos = 0; } } public int getDepth() { throw new UnsupportedOperationException(); } public boolean getFeature(String name) { throw new UnsupportedOperationException(); } public String getName() { throw new UnsupportedOperationException(); } public String getNamespace() { throw new UnsupportedOperationException(); } public String getPrefix(String namespace, boolean generatePrefix) throws IllegalArgumentException { throw new UnsupportedOperationException(); } public Object getProperty(String name) { throw new UnsupportedOperationException(); } public void ignorableWhitespace(String text) throws IOException, IllegalArgumentException, IllegalStateException { throw new UnsupportedOperationException(); } public void processingInstruction(String text) throws IOException, IllegalArgumentException, IllegalStateException { throw new UnsupportedOperationException(); } public void setFeature(String name, boolean state) throws IllegalArgumentException, IllegalStateException { if (name.equals("​​http://xmlpull.org/v1/doc/features.html#indent-output​​​")) { return; } throw new UnsupportedOperationException(); } public void setOutput(OutputStream os, String encoding) throws IOException, IllegalArgumentException, IllegalStateException { if (os == null) throw new IllegalArgumentException(); if (true) { try { mCharset = Charset.forName(encoding).newEncoder(); } catch (IllegalCharsetNameException e) { throw (UnsupportedEncodingException) (new UnsupportedEncodingException(encoding).initCause(e)); } catch (UnsupportedCharsetException e) { throw (UnsupportedEncodingException) (new UnsupportedEncodingException(encoding).initCause(e)); } mOutputStream = os; } else { setOutput(encoding == null ? new OutputStreamWriter(os) : new OutputStreamWriter(os, encoding)); } } public void setOutput(Writer writer) throws IOException, IllegalArgumentException, IllegalStateException { mWriter = writer; } public void setPrefix(String prefix, String namespace) throws IOException, IllegalArgumentException, IllegalStateException { throw new UnsupportedOperationException(); } public void setProperty(String name, Object value) throws IllegalArgumentException, IllegalStateException { throw new UnsupportedOperationException(); } public void startDocument(String encoding, Boolean standalone) throws IOException, IllegalArgumentException, IllegalStateException { append("<?xml version='1.0' encoding='utf-8' standalone='" + (standalone ? "yes" : "no") + "' ?>\n"); } public XmlSerializer startTag(String namespace, String name) throws IOException, IllegalArgumentException, IllegalStateException { if (mInTag) { append(">\n"); } append('<'); if (namespace != null) { append(namespace); append(':'); } append(name); mInTag = true; return this; } public XmlSerializer text(char[] buf, int start, int len) throws IOException, IllegalArgumentException, IllegalStateException { if (mInTag) { append(">"); mInTag = false; } escapeAndAppendString(buf, start, len); return this; } public XmlSerializer text(String text) throws IOException, IllegalArgumentException, IllegalStateException { if (mInTag) { append(">"); mInTag = false; } escapeAndAppendString(text); return this; } } 2. 工具类:XmlUtils.java package com.xml.demo.utils; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import android.util.Xml; // copy from frameworks/base/core/java/com/android/internal/util/XmlUtils.java 1.6 public class XmlUtils { public static void skipCurrentTag(XmlPullParser parser) throws XmlPullParserException, IOException { int outerDepth = parser.getDepth(); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { } } public static final int convertValueToList(CharSequence value, String[] options, int defaultValue) { if (null != value) { for (int i = 0; i < options.length; i++) { if (value.equals(options[i])) return i; } } return defaultValue; } public static final boolean convertValueToBoolean(CharSequence value, boolean defaultValue) { boolean result = false; if (null == value) return defaultValue; if (value.equals("1") || value.equals("true") || value.equals("TRUE")) result = true; return result; } public static final int convertValueToInt(CharSequence charSeq, int defaultValue) { if (null == charSeq) return defaultValue; String nm = charSeq.toString(); // XXX This code is copied from Integer.decode() so we don't // have to instantiate an Integer! int value; int sign = 1; int index = 0; int len = nm.length(); int base = 10; if ('-' == nm.charAt(0)) { sign = -1; index++; } if ('0' == nm.charAt(index)) { // Quick check for a zero by itself if (index == (len - 1)) return 0; char c = nm.charAt(index + 1); if ('x' == c || 'X' == c) { index += 2; base = 16; } else { index++; base = 8; } } else if ('#' == nm.charAt(index)) { index++; base = 16; } return Integer.parseInt(nm.substring(index), base) * sign; } public static final int convertValueToUnsignedInt(String value, int defaultValue) { if (null == value) return defaultValue; return parseUnsignedIntAttribute(value); } public static final int parseUnsignedIntAttribute(CharSequence charSeq) { String value = charSeq.toString(); long bits; int index = 0; int len = value.length(); int base = 10; if ('0' == value.charAt(index)) { // Quick check for zero by itself if (index == (len - 1)) return 0; char c = value.charAt(index + 1); if ('x' == c || 'X' == c) { // check for hex index += 2; base = 16; } else { // check for octal index++; base = 8; } } else if ('#' == value.charAt(index)) { index++; base = 16; } return (int) Long.parseLong(value.substring(index), base); } /** * Flatten a Map into an output stream as XML. The map can later be read * back with readMapXml(). * * @param val * The map to be flattened. * @param out * Where to write the XML data. * * @see #writeMapXml(Map, String, XmlSerializer) * @see #writeListXml * @see #writeValueXml * @see #readMapXml */ public static final void writeMapXml(Map val, OutputStream out) throws XmlPullParserException, java.io.IOException { XmlSerializer serializer = new FastXmlSerializer(); serializer.setOutput(out, "utf-8"); serializer.startDocument(null, true); serializer.setFeature("​​http://xmlpull.org/v1/doc/features.html#indent-output​​​", true); writeMapXml(val, null, serializer); serializer.endDocument(); } /** * Flatten a List into an output stream as XML. The list can later be read * back with readListXml(). * * @param val * The list to be flattened. * @param out * Where to write the XML data. * * @see #writeListXml(List, String, XmlSerializer) * @see #writeMapXml * @see #writeValueXml * @see #readListXml */ public static final void writeListXml(List val, OutputStream out) throws XmlPullParserException, java.io.IOException { XmlSerializer serializer = Xml.newSerializer(); serializer.setOutput(out, "utf-8"); serializer.startDocument(null, true); serializer.setFeature("​​http://xmlpull.org/v1/doc/features.html#indent-output​​​", true); writeListXml(val, null, serializer); serializer.endDocument(); } /** * Flatten a Map into an XmlSerializer. The map can later be read back with * readThisMapXml(). * * @param val * The map to be flattened. * @param name * Name attribute to include with this list's tag, or null for * none. * @param out * XmlSerializer to write the map into. * * @see #writeMapXml(Map, OutputStream) * @see #writeListXml * @see #writeValueXml * @see #readMapXml */ public static final void writeMapXml(Map val, String name, XmlSerializer out) throws XmlPullParserException, java.io.IOException { if (val == null) { out.startTag(null, "null"); out.endTag(null, "null"); return; } Set s = val.entrySet(); Iterator i = s.iterator(); out.startTag(null, "map"); if (name != null) { out.attribute(null, "name", name); } while (i.hasNext()) { Map.Entry e = (Map.Entry) i.next(); writeValueXml(e.getValue(), (String) e.getKey(), out); } out.endTag(null, "map"); } /** * Flatten a List into an XmlSerializer. The list can later be read back * with readThisListXml(). * * @param val * The list to be flattened. * @param name * Name attribute to include with this list's tag, or null for * none. * @param out * XmlSerializer to write the list into. * * @see #writeListXml(List, OutputStream) * @see #writeMapXml * @see #writeValueXml * @see #readListXml */ public static final void writeListXml(List val, String name, XmlSerializer out) throws XmlPullParserException, java.io.IOException { if (val == null) { out.startTag(null, "null"); out.endTag(null, "null"); return; } out.startTag(null, "list"); if (name != null) { out.attribute(null, "name", name); } int N = val.size(); int i = 0; while (i < N) { writeValueXml(val.get(i), null, out); i++; } out.endTag(null, "list"); } /** * Flatten a byte[] into an XmlSerializer. The list can later be read back * with readThisByteArrayXml(). * * @param val * The byte array to be flattened. * @param name * Name attribute to include with this array's tag, or null for * none. * @param out * XmlSerializer to write the array into. * * @see #writeMapXml * @see #writeValueXml */ public static final void writeByteArrayXml(byte[] val, String name, XmlSerializer out) throws XmlPullParserException, java.io.IOException { if (val == null) { out.startTag(null, "null"); out.endTag(null, "null"); return; } out.startTag(null, "byte-array"); if (name != null) { out.attribute(null, "name", name); } final int N = val.length; out.attribute(null, "num", Integer.toString(N)); StringBuilder sb = new StringBuilder(val.length * 2); for (int i = 0; i < N; i++) { int b = val[i]; int h = b >> 4; sb.append(h >= 10 ? ('a' + h - 10) : ('0' + h)); h = b & 0xff; sb.append(h >= 10 ? ('a' + h - 10) : ('0' + h)); } out.text(sb.toString()); out.endTag(null, "byte-array"); } /** * Flatten an int[] into an XmlSerializer. The list can later be read back * with readThisIntArrayXml(). * * @param val * The int array to be flattened. * @param name * Name attribute to include with this array's tag, or null for * none. * @param out * XmlSerializer to write the array into. * * @see #writeMapXml * @see #writeValueXml * @see #readThisIntArrayXml */ public static final void writeIntArrayXml(int[] val, String name, XmlSerializer out) throws XmlPullParserException, java.io.IOException { if (val == null) { out.startTag(null, "null"); out.endTag(null, "null"); return; } out.startTag(null, "int-array"); if (name != null) { out.attribute(null, "name", name); } final int N = val.length; out.attribute(null, "num", Integer.toString(N)); for (int i = 0; i < N; i++) { out.startTag(null, "item"); out.attribute(null, "value", Integer.toString(val[i])); out.endTag(null, "item"); } out.endTag(null, "int-array"); } /** * Flatten an object's value into an XmlSerializer. The value can later be * read back with readThisValueXml(). * * Currently supported value types are: null, String, Integer, Long, Float, * Double Boolean, Map, List. * * @param v * The object to be flattened. * @param name * Name attribute to include with this value's tag, or null for * none. * @param out * XmlSerializer to write the object into. * * @see #writeMapXml * @see #writeListXml * @see #readValueXml */ public static final void writeValueXml(Object v, String name, XmlSerializer out) throws XmlPullParserException, java.io.IOException { String typeStr; if (v == null) { out.startTag(null, "null"); if (name != null) { out.attribute(null, "name", name); } out.endTag(null, "null"); return; } else if (v instanceof String) { out.startTag(null, "string"); if (name != null) { out.attribute(null, "name", name); } out.text(v.toString()); out.endTag(null, "string"); return; } else if (v instanceof Integer) { typeStr = "int"; } else if (v instanceof Long) { typeStr = "long"; } else if (v instanceof Float) { typeStr = "float"; } else if (v instanceof Double) { typeStr = "double"; } else if (v instanceof Boolean) { typeStr = "boolean"; } else if (v instanceof byte[]) { writeByteArrayXml((byte[]) v, name, out); return; } else if (v instanceof int[]) { writeIntArrayXml((int[]) v, name, out); return; } else if (v instanceof Map) { writeMapXml((Map) v, name, out); return; } else if (v instanceof List) { writeListXml((List) v, name, out); return; } else if (v instanceof CharSequence) { // XXX This is to allow us to at least write something if // we encounter styled text... but it means we will drop all // of the styling information. :( out.startTag(null, "string"); if (name != null) { out.attribute(null, "name", name); } out.text(v.toString()); out.endTag(null, "string"); return; } else { throw new RuntimeException("writeValueXml: unable to write value " + v); } out.startTag(null, typeStr); if (name != null) { out.attribute(null, "name", name); } out.attribute(null, "value", v.toString()); out.endTag(null, typeStr); } /** * Read a HashMap from an InputStream containing XML. The stream can * previously have been written by writeMapXml(). * * @param in * The InputStream from which to read. * * @return HashMap The resulting map. * * @see #readListXml * @see #readValueXml * @see #readThisMapXml #see #writeMapXml */ public static final HashMap readMapXml(InputStream in) throws XmlPullParserException, java.io.IOException { XmlPullParser parser = Xml.newPullParser(); parser.setInput(in, null); return (HashMap) readValueXml(parser, new String[1]); } /** * Read an ArrayList from an InputStream containing XML. The stream can * previously have been written by writeListXml(). * * @param in * The InputStream from which to read. * * @return HashMap The resulting list. * * @see #readMapXml * @see #readValueXml * @see #readThisListXml * @see #writeListXml */ public static final ArrayList readListXml(InputStream in) throws XmlPullParserException, java.io.IOException { XmlPullParser parser = Xml.newPullParser(); parser.setInput(in, null); return (ArrayList) readValueXml(parser, new String[1]); } /** * Read a HashMap object from an XmlPullParser. The XML data could * previously have been generated by writeMapXml(). The XmlPullParser must * be positioned <em>after</em> the tag that begins the map. * * @param parser * The XmlPullParser from which to read the map data. * @param endTag * Name of the tag that will end the map, usually "map". * @param name * An array of one string, used to return the name attribute of * the map's tag. * * @return HashMap The newly generated map. * * @see #readMapXml */ public static final HashMap readThisMapXml(XmlPullParser parser, String endTag, String[] name) throws XmlPullParserException, java.io.IOException { HashMap map = new HashMap(); int eventType = parser.getEventType(); do { if (eventType == parser.START_TAG) { Object val = readThisValueXml(parser, name); if (name[0] != null) { // System.out.println("Adding to map: " + name + " -> " + // val); map.put(name[0], val); } else { throw new XmlPullParserException("Map value without name attribute: " + parser.getName()); } } else if (eventType == parser.END_TAG) { if (parser.getName().equals(endTag)) { return map; } throw new XmlPullParserException("Expected " + endTag + " end tag at: " + parser.getName()); } eventType = parser.next(); } while (eventType != parser.END_DOCUMENT); throw new XmlPullParserException("Document ended before " + endTag + " end tag"); } /** * Read an ArrayList object from an XmlPullParser. The XML data could * previously have been generated by writeListXml(). The XmlPullParser must * be positioned <em>after</em> the tag that begins the list. * * @param parser * The XmlPullParser from which to read the list data. * @param endTag * Name of the tag that will end the list, usually "list". * @param name * An array of one string, used to return the name attribute of * the list's tag. * * @return HashMap The newly generated list. * * @see #readListXml */ public static final ArrayList readThisListXml(XmlPullParser parser, String endTag, String[] name) throws XmlPullParserException, java.io.IOException { ArrayList list = new ArrayList(); int eventType = parser.getEventType(); do { if (eventType == parser.START_TAG) { Object val = readThisValueXml(parser, name); list.add(val); // System.out.println("Adding to list: " + val); } else if (eventType == parser.END_TAG) { if (parser.getName().equals(endTag)) { return list; } throw new XmlPullParserException("Expected " + endTag + " end tag at: " + parser.getName()); } eventType = parser.next(); } while (eventType != parser.END_DOCUMENT); throw new XmlPullParserException("Document ended before " + endTag + " end tag"); } /** * Read an int[] object from an XmlPullParser. The XML data could previously * have been generated by writeIntArrayXml(). The XmlPullParser must be * positioned <em>after</em> the tag that begins the list. * * @param parser * The XmlPullParser from which to read the list data. * @param endTag * Name of the tag that will end the list, usually "list". * @param name * An array of one string, used to return the name attribute of * the list's tag. * * @return Returns a newly generated int[]. * * @see #readListXml */ public static final int[] readThisIntArrayXml(XmlPullParser parser, String endTag, String[] name) throws XmlPullParserException, java.io.IOException { int num; try { num = Integer.parseInt(parser.getAttributeValue(null, "num")); } catch (NullPointerException e) { throw new XmlPullParserException("Need num attribute in byte-array"); } catch (NumberFormatException e) { throw new XmlPullParserException("Not a number in num attribute in byte-array"); } int[] array = new int[num]; int i = 0; int eventType = parser.getEventType(); do { if (eventType == parser.START_TAG) { if (parser.getName().equals("item")) { try { array[i] = Integer.parseInt(parser.getAttributeValue(null, "value")); } catch (NullPointerException e) { throw new XmlPullParserException("Need value attribute in item"); } catch (NumberFormatException e) { throw new XmlPullParserException("Not a number in value attribute in item"); } } else { throw new XmlPullParserException("Expected item tag at: " + parser.getName()); } } else if (eventType == parser.END_TAG) { if (parser.getName().equals(endTag)) { return array; } else if (parser.getName().equals("item")) { i++; } else { throw new XmlPullParserException("Expected " + endTag + " end tag at: " + parser.getName()); } } eventType = parser.next(); } while (eventType != parser.END_DOCUMENT); throw new XmlPullParserException("Document ended before " + endTag + " end tag"); } /** * Read a flattened object from an XmlPullParser. The XML data could * previously have been written with writeMapXml(), writeListXml(), or * writeValueXml(). The XmlPullParser must be positioned <em>at</em> the tag * that defines the value. * * @param parser * The XmlPullParser from which to read the object. * @param name * An array of one string, used to return the name attribute of * the value's tag. * * @return Object The newly generated value object. * * @see #readMapXml * @see #readListXml * @see #writeValueXml */ public static final Object readValueXml(XmlPullParser parser, String[] name) throws XmlPullParserException, java.io.IOException { int eventType = parser.getEventType(); do { if (eventType == parser.START_TAG) { return readThisValueXml(parser, name); } else if (eventType == parser.END_TAG) { throw new XmlPullParserException("Unexpected end tag at: " + parser.getName()); } else if (eventType == parser.TEXT) { throw new XmlPullParserException("Unexpected text: " + parser.getText()); } eventType = parser.next(); } while (eventType != parser.END_DOCUMENT); throw new XmlPullParserException("Unexpected end of document"); } private static final Object readThisValueXml(XmlPullParser parser, String[] name) throws XmlPullParserException, java.io.IOException { final String valueName = parser.getAttributeValue(null, "name"); final String tagName = parser.getName(); // System.out.println("Reading this value tag: " + tagName + ", name=" + // valueName); Object res; if (tagName.equals("null")) { res = null; } else if (tagName.equals("string")) { String value = ""; int eventType; while ((eventType = parser.next()) != parser.END_DOCUMENT) { if (eventType == parser.END_TAG) { if (parser.getName().equals("string")) { name[0] = valueName; // System.out.println("Returning value for " + valueName // + ": " + value); return value; } throw new XmlPullParserException("Unexpected end tag in <string>: " + parser.getName()); } else if (eventType == parser.TEXT) { value += parser.getText(); } else if (eventType == parser.START_TAG) { throw new XmlPullParserException("Unexpected start tag in <string>: " + parser.getName()); } } throw new XmlPullParserException("Unexpected end of document in <string>"); } else if (tagName.equals("int")) { res = Integer.parseInt(parser.getAttributeValue(null, "value")); } else if (tagName.equals("long")) { res = Long.valueOf(parser.getAttributeValue(null, "value")); } else if (tagName.equals("float")) { res = new Float(parser.getAttributeValue(null, "value")); } else if (tagName.equals("double")) { res = new Double(parser.getAttributeValue(null, "value")); } else if (tagName.equals("boolean")) { res = Boolean.valueOf(parser.getAttributeValue(null, "value")); } else if (tagName.equals("int-array")) { parser.next(); res = readThisIntArrayXml(parser, "int-array", name); name[0] = valueName; // System.out.println("Returning value for " + valueName + ": " + // res); return res; } else if (tagName.equals("map")) { parser.next(); res = readThisMapXml(parser, "map", name); name[0] = valueName; // System.out.println("Returning value for " + valueName + ": " + // res); return res; } else if (tagName.equals("list")) { parser.next(); res = readThisListXml(parser, "list", name); name[0] = valueName; // System.out.println("Returning value for " + valueName + ": " + // res); return res; } else { throw new XmlPullParserException("Unknown tag: " + tagName); } // Skip through to end tag. int eventType; while ((eventType = parser.next()) != parser.END_DOCUMENT) { if (eventType == parser.END_TAG) { if (parser.getName().equals(tagName)) { name[0] = valueName; // System.out.println("Returning value for " + valueName + // ": " + res); return res; } throw new XmlPullParserException("Unexpected end tag in <" + tagName + ">: " + parser.getName()); } else if (eventType == parser.TEXT) { throw new XmlPullParserException("Unexpected text in <" + tagName + ">: " + parser.getName()); } else if (eventType == parser.START_TAG) { throw new XmlPullParserException("Unexpected start tag in <" + tagName + ">: " + parser.getName()); } } throw new XmlPullParserException("Unexpected end of document in <" + tagName + ">"); } public static final void beginDocument(XmlPullParser parser, String firstElementName) throws XmlPullParserException, IOException { int type; while ((type = parser.next()) != parser.START_TAG && type != parser.END_DOCUMENT) { ; } if (type != parser.START_TAG) { throw new XmlPullParserException("No start tag found"); } if (!parser.getName().equals(firstElementName)) { throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() + ", expected " + firstElementName); } } public static final void nextElement(XmlPullParser parser) throws XmlPullParserException, IOException { int type; while ((type = parser.next()) != parser.START_TAG && type != parser.END_DOCUMENT) { ; } } } 3. MainActivity.java package com.xml.demo; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import org.xmlpull.v1.XmlSerializer; import android.app.Activity; import android.content.res.AssetManager; import android.os.Bundle; import android.util.Log; import com.xml.demo.utils.FastXmlSerializer; public class MainActivity extends Activity { String TAG = "ZZMainActivity"; private AssetManager mAssetManager; private InputStream mInputStream; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mAssetManager = getApplicationContext().getAssets(); try { mInputStream = mAssetManager.open("test.xml"); } catch (IOException e) { e.printStackTrace(); } testSaveXmlWithUtils(); } private void testSaveXmlWithUtils() { // 生成xml文件 List<Person> persons = new ArrayList<Person>(); persons.add(new Person(80, "zhangsan", (short) 13)); persons.add(new Person(30, "wangwu", (short) 23)); persons.add(new Person(70, "zhaoliu", (short) 33)); try { File file = new File(this.getFilesDir(), "toXmlFile.xml"); FileOutputStream str = new FileOutputStream(file); XmlSerializer serializer = new FastXmlSerializer(); serializer.setOutput(str, "utf-8"); serializer.startDocument(null, true); serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output​", true); serializer.startTag(null, "persons"); for (Person person : persons) { serializer.startTag(null, "person"); serializer.attribute(null, "id", person.getId().toString()); serializer.startTag(null, "name"); serializer.text(person.getName()); serializer.endTag(null, "name"); serializer.startTag(null, "age"); serializer.text(person.getAge().toString()); serializer.endTag(null, "age"); serializer.endTag(null, "person"); } serializer.endTag(null, "persons"); serializer.endDocument(); str.flush(); str.close(); } catch (IOException e) { Log.w(TAG, "IOException:", e); } } } /data/data/com.xml.demo/files/toXmlFile.xml 文件: <?xml version='1.0' encoding='utf-8' standalone='yes' ?> <persons> <person id="80"> <name>zhangsan</name> <age>13</age> </person> <person id="30"> <name>wangwu</name> <age>23</age> </person> <person id="70"> <name>zhaoliu</name> <age>33</age> </person> </persons> 可以看到,通过上述工具类生成的xml文件,没有在同一行显示,比较格式化,看起来比较阅读。此时读取xml文件内容,可以参考 PullParserHandler类的getPersons()方法实现。 五、SAX、DOM、Pull的比较 1. 内存占用: SAX、Pull 优于DOM 2. 编程方式 推荐 Pull 3.访问方式 SAX、Pull解析的方式是同步的,即解析器读到哪里,就对哪里进行处理。而DOM是已经将文件解析好后,供用户读取XML中感兴趣的内容。 个人总结: 推荐使用Android提供的FastXmlSerializer.java和XmlUtils.java来生成xml文件,读取xml文件按照Pull的方式读取即可