最终效果图
代码结构图:
代码详解:
main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/textView" /> </LinearLayout>
beauties.xml
<?xml version="1.0" encoding="utf-8"?> <beauties > <beauty> <name>林志玲</name> <age>28</age> </beauty> <beauty> <name>杨幂</name> <age>23</age> </beauty> </beauties>
activity的代码
package cn.com.sax; import java.io.InputStream; import java.util.ArrayList; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.XMLReader; import org.xml.sax.helpers.XMLReaderFactory; import android.app.Activity; import android.os.Bundle; import android.sax.Element; import android.sax.EndElementListener; import android.sax.EndTextElementListener; import android.sax.RootElement; import android.sax.StartElementListener; import android.util.Log; import android.util.Xml; import android.widget.TextView; /** * @author chenzheng_java * @description 使用android.sax包下的类进行sax解析 */ public class AndroidSaxActivity extends Activity { private String result = "最终结果:/n"; private ArrayList<Beauty> beautyList = new ArrayList<Beauty>(); private Beauty beauty = null ; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); /*以下XMLReader的这种解析方式也是属于sax解析, * 同我们使用SaxParser功能一样。但是这种功能更加的强大. */ try { Log.i("通知", "开始解析"); InputStream inputStream = this.getClassLoader().getResourceAsStream("beauties.xml"); SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParser parser = factory.newSAXParser(); XMLReader reader = parser.getXMLReader(); reader.setContentHandler(getRootElement().getContentHandler()); reader.parse(new InputSource(inputStream)); Log.i("通知", "解析完毕"); } catch (Exception e) { e.printStackTrace(); } for(Beauty beauty:beautyList){ result+=beauty.toString()+"/n"; } TextView textView = (TextView) this.findViewById(R.id.textView); textView.setText(result); } /** * * @return 返回设置好处理机制的rootElement */ private RootElement getRootElement(){ /*rootElement代表着根节点,参数为根节点的tagName*/ RootElement rootElement = new RootElement("beauties"); /*获取一类子节点,并为其设置相应的事件 * 这里需要注意,虽然我们只设置了一次beauty的事件,但是我们文档中根节点下的所有 * beauty却都可以触发这个事件。 * */ Element beautyElement = rootElement.getChild("beauty"); // 读到元素开始位置时触发,如读到<beauty>时 beautyElement.setStartElementListener(new StartElementListener() { @Override public void start(Attributes attributes) { Log.i("通知", "start"); beauty = new Beauty(); } }); //读到元素结束位置时触发,如读到</beauty>时 beautyElement.setEndElementListener(new EndElementListener() { @Override public void end() { beautyList.add(beauty); } }); Element nameElement = beautyElement.getChild("name"); // 读到文本的末尾时触发,这里的body即为文本的内容部分 nameElement.setEndTextElementListener(new EndTextElementListener() { @Override public void end(String body) { beauty.setName(body); } }); Element ageElement = beautyElement.getChild("age"); ageElement.setEndTextElementListener(new EndTextElementListener() { @Override public void end(String body) { beauty.setAge(body); } }); return rootElement; } private class Beauty { String name; String age; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } @Override public String toString() { return "美女资料 [年龄=" + age + ", 姓名=" + name + "]"; } } }
运行便可以得到上面的代码。
------------------------------------------
这里我们来谈一下,android为我们提供的一些和解析xml有关的类。
android.sax包。下面类和接口的主要结构如图所示
类主要有两个,Element和RootElement。这里大家一定要注意的一点是,在使用类的时候一定要看准你导入的是什么包,千万不要倒错包哦,否则你就等着在痛苦和郁闷中死去吧。
其中RootElement是继承自Element类的,只是用来代表document的根节点。它的方法基本上都是继承自Element类的。它只定义了一个方法getContentHandler() ,该方法返回一个sax的contentHandler对象。
(这里估计有人迷糊了,sax解析不是事件驱动模式吗,怎么还跟dom中才有的根节点扯上关系了。其实答案很简单,最底层还是按照sax的方式进行解析的,这里只是为了方便用户使用,android才提供了这么几个类似和节点有关的方法,让我们可以用节点的思维去操作sax解析,毕竟节点用起来条理性要比sax的事件驱动有条理性多了)。
Element类的主要方法有
其他的都没什么可说的,我就说一下getChild方法和requireChild方法的区别和联系,俩个方法都是通过tagName(标签名)获取当前element元素的子元素的。在源代码中,requireChild是这样实现的 public Element requireChild(String uri, String localName) { Element child = getChild(uri, localName); if (requiredChilden == null) { requiredChilden = new ArrayList<Element>(); requiredChilden.add(child); } else { if (!requiredChilden.contains(child)) { requiredChilden.add(child); } } return child; }
其中requiredChilden是一个arrayList<Element>,这里面存放着所有你访问过的元素。这两者最大的区别就是,当我们使用requireChild方法时,在我们解析的过程中,如果某一个访问过的Element突然消失了(鬼知道是什么原因),该方法会通过抛出异常的方式通知用户。
------------------------------------------------------------------------
从代码中我们可以看到,我们通过设置RootElement以及其子节点的事件监听器,来规定解析xml时应该执行的操作;然后通过getContentHandler方法可以获取一个ContentHandler对象。然后通过xmlreader的setContentHandler方法将其与解析器关联起来。
------------------------------------------------------------------------
其实,这里我们还可以用这样的语句来进行解析Xml.parse(inputStream, Xml.Encoding.UTF_8, getRootElement().getContentHandler());这个XML类是android为我们提供的帮助类。在后面我们将详细的介绍。
------------------------------------------------------------------------
我们都知道通过SAXParser对象解析xml的方式,这里我们又从代码中看到了利用另一个对象XMLReader进行解析,那么两者到底有什么联系和区别呢?
其实SAXParser是在SAX 1.0 定义的,而XMLReader则是在2.0中才开始出现的。你可以认为XMLReader的出现是为了替代SAXParser解析的,两者本质上干的事情是一样的,只不过XMLReader的功能更加的强悍而已。
关于XMLReader的获取方式,除了通过SAXParser的getXMLReader方法获得之外,我们还可以通过以下两种方式。
XMLReader parser=XMLReaderFactory.createXMLReader(); (1)
XMLReader parser=XMLReaderFactory.createXMLReader(String className); (2)
下面附上一段介绍:
In fact,XMLReader is an interface,so XMLReaderFactory needs a concrete class which implements this interface to get an instance,once the factory get the class's name ,then it use java reflection to create an instantiated object and return it,org.apache.xerces.parsers.SAXParser is such a class in case. if you try to use api (1) to get an XMLReader instance, XMLReaderFactory first try to get the XMLReader's classname by retrieving the system property with key "org.xml.sax.driver", if this property has set with an appropriate classname,the factory use this name instantiated an object and return; if this property not set,then it fallback to class ParserFactory, ParserFactory then retrieves a system property named "org.xml.sax.parser" to get a parser's classname to instantiate ,if this property is not set too,your invocation to get a XMLreader will fail and a SAXException with the message "System property org.xml.sax.driver not specified" is throwed,otherwise,the ParserFactory will return an parser instance with the specified parser name to XMLReaderFactory,and XMLReaderFactory use this parser to instantiate an ParserAdapter like new ParserAdapter(parser), so the XMLReader you get is an instance of the classnew ParserAdapter. so,in conclusion,in order to make it works as fine as you expected,you can do as the following ways to get an XMLReader: (1) XMLReader parser=XMLReaderFactory.createXMLReader(String className); (2) System.setProperty("org.xml.sax.driver","org.apache.xerces.parsers.SAXParser"); XMLReader parser=XMLReaderFactory.createXMLReader(); (3) System.setProperty("org.xml.sax.parser","org.apache.xerces.parsers.SAXParser"); XMLReader parser=XMLReaderFactory.createXMLReader(); (4) more directly XMLReader parser=new org.apache.xerces.parsers.SAXParser(); 翻译如下:
事实上,XMLReader是一个接口,所以想要生成一个XMLReader对象的话,我们的XMLReaderFactory需要关联一个实现了该接口的类。XMLReaderFactory一旦获得了实现了该接口的那个类的名称,就会通过java的反射机制创建一个XMLReader对象返回给我们。
实际上,org.apache.xerces.parsers.SAXParser 就是一个满足了这些条件的类。
如果你想通过XMLReader parser=XMLReaderFactory.createXMLReader(String className); 的方式获得一个XMLReader对象,XMLReaderFactory 首先会先去系统的property中寻找名称为"org.xml.sax.driver"的属性,如果该属性存在,则以该属性的值作为classname去实例化一个XMLReader对象;如果该属性没有设置,则跳转到ParserFactory这个类,通过访问一个名称为"org.xml.sax.parser"的系统变量来进行实例化;如果这个org.xml.sax.parser变量也没有被设置,操作就会失败然后跑出异常。如果org.xml.sax.parser存在,ParserFactory将会返回一个特定解析器的对象给XMLReaderFactory,XMLReaderFactory则通过得到的解析器对象去实例化一个PaserAdapter,所以从最底层看,我们得到的XMLReader对象就是一个PaserAdapter的对象。