已经好久没有更新博客了,炎热的夏天正如我的心情一样枯燥乏味,今年是互联的寒冬,作为一个菜鸟已经深深的感觉到了,因为自己失业了。从去年的年末的一些听闻,自己竟没有准备好迎接失业,以至于一年了,只比以前强了一点。没错,自我感觉就是一个点,非常渺小的一个小点。以至于本痛彻心扉告别了自己喜欢的LoL,这也许是个时机把,让自己的心沉淀下来。
人们常说干一行,爱一行,自己想了一会儿,真的很想打自己。自己做的只是应付差事,甚至是喜欢都谈不上,又怎么能不成现在这个样子呢!我之前一直在想,自己如果把玩游戏的精力、时间、喜好程度换到学习代码上面,那么肯定过的比现在好。至少失业后能立即找到工作,或者是面试量是现在的好几倍,好了不多说了,自己正在改邪归正中。
上一篇写到了用dom来解析Andorid中的xml文件,今天看看sax这个解析,顺别把Java转义符号做个表格展示一下。
SAX(Simple API for XML)是一个解析速度快并且占用内存少的XML解析器,非常适合用于Android等移动设备。
SAX解析器是一种基于事件的解析器,事件驱动的流式解析方式是,从文件的开始顺序解析到文档的结束,不可暂停或倒退。它的核心是事件处理模式,主要是围绕着事件源以及事件处理器来工作的。当事件源产生事件后,调用事件处理器相应的处理方式,一个事件就可以得到处理。在事件源调用事件处理器中特定方法的时候,还要传递给事件处理器相应事件的状态信息,这样事件处理器才能够根据提供的事件信息来决定自己的行为。并且,它并不需要解析完整个文档,在按内容顺序解析文档的过程中,SAX会判断当前读到的字符是否合法XML语法中的某部分,如果符合就会触发事件。所谓事件,其实就是一些回调(callback)方法,这些方法(事件)定义在ContentHandler接口。
在SAX接口中,事件源是org.xml.sax包中的XMLReader,它通过parser()方法来解析XML文档,并产生事件。事件处理器是org.xml.sax包中ContentHandle、DTDHandler、ErrorHandler以及EntityResolver这4个接口。XMLReader通过相应事件处理器注册方法setXXX()来完成的与ContentHandle、DTDHandler、ErrorHandler以及EntityResolver这4个接口的连接。
什么是事件驱动模式?它将XML文档转换成一系列的事件,由单独的事件处理器来决定如何处理。一个可以产生事件的对象叫做事件源,而一个可以针对事件做出响应的对象就被叫做事件处理器。
优点:不用实现调入整个文档,占用资源少。尤其在嵌入式环境中,如android,极力推荐使用SAX解析。
缺点:不像DOM解析一样将文档长期驻留在内存中,数据不是持久的。如果事件过后没有保存数据,数据就会丢失。
使用场合:机器有性能限制。
下面是大致使用的方法
//用于处理文档解析开始事件
public
void
startDocument()
throws
SAXException
//处理元素开始事件,从参数中可以获得元素所在名称空间的uri,元素名称,属性类表等信息
public
void
startElement(String namespacesURI , String localName , String qName , Attributes atts)
throws
SAXException
//处理元素结束事件,从参数中可以获得元素所在名称空间的uri,元素名称等信息
public
void
endElement(String namespacesURI , String localName , String qName)
throws
SAXException
//处理元素的字符内容,从参数中可以获得内容
public
void
characters(
char
[] ch ,
int
start ,
int
length)
throws
SAXException
我们创建一个项目
在上一篇中有一个错误,assets文件夹应该创建在main下面,下面列出创建步骤
创建完成之后我们便写入数据
<?xml version="1.0" encoding="UTF-8"?>
<gen>
<layer>
<type>\ddd</type>
<value>1~3位八进制数据所表示的字符,如 \456</value>
</layer>
<layer>
<type>\dxxxx</type>
<value>4~16位十六进制数据所表示的字符,如 \0052</value>
</layer>
<layer>
<type>\'</type>
<value>单引号字符</value>
</layer>
<layer>
<type>\\</type>
<value>反斜杠字符</value>
</layer>
<layer>
<type>\t</type>
<value>垂直制表符,将光标移到下一个制表符的位置</value>
</layer>
<layer>
<type>\r</type>
<value>回车</value>
</layer>
<layer>
<type>\n</type>
<value>换行</value>
</layer>
<layer>
<type>\b</type>
<value>退格</value>
</layer>
<layer>
<type>\f</type>
<value>换页</value>
</layer>
</gen>
然后我们建立一个实体类便于操作
public class Data { public String getType() { return type; } public void setType(String type) { this.type = type; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } private String type; private String value; @Override public String toString() { return "type='" + type + '\'' + ", value='" + v
public class ContenHanler extends DefaultHandler { private List<Data> datas = null; private Data currenData; private String tagName; public List<Data> getData() { return datas; }
我们接着继续创建解析解析类 //当遇到文档的开头的时候,调用这个方法,可以在其中做一些预处理的工作。 @Override public void startDocument() throws SAXException { super.startDocument(); datas = new ArrayList<>(); } //当读到一个开始标签的时候,会触发这个方法,再次获得元素的属性。namespaceURI就是命名空间,localName是不带命名空间前缀的标签名,qName是带命名空间前缀的标签名。通过attributes可以得到所有的属性名和相应的值。要注意的是SAX中一个重要的特点就是它的流式处理,当遇到一个标签的时候,它并不会纪录下以前所碰到的标签,也就是说,在startElement()方法中,所有你所知道的信息,就是标签的名字和属性,至于标签的嵌套结构,上层标签的名字,是否有子元属等等其它与结构相关的信息,都是不得而知的,都需要你的程序来完成。这使得SAX在编程处理上没有DOM来得那么方便。 @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { super.startElement(uri, localName, qName, attributes); // 开始解析根节点 if (qName.equals("layer")) { currenData = new Data(); currenData.setType(attributes.getType("type")); } this.tagName = localName; } //这个方法用来处理在XML文件中读到的内容,第一个参数用于存放文件的内容,后面两个参数是读到的字符串在这个数组中的起始位置和长度,使用new String(ch,start,length)就可以获取内容。 @Override public void characters(char[] ch, int start, int length) throws SAXException { super.characters(ch, start, length); if (tagName != null) { String da = new String(ch, start, length); if (tagName.equals("type")) { this.currenData.setType(da); } else if (tagName.equals("value")) { this.currenData.setValue(String.valueOf(da)); } } } //和startDocument()方法相对应。当文档结束的时候,调用这个方法,可以在其中做一些善后的工作。 @Override public void endElement(String uri, String localName, String qName) throws SAXException { super.endElement(uri, localName, qName); if (localName.equals("layer")) { datas.add(currenData); currenData = null; } this.tagName = null; } }
alue; }}
新建类SaxService实例化一个SAX解析器的工厂:SaxParserUtils
需要一个调用SAXParser对象的类,这里新建一个SaxParserUtils类,实例化SAXParserFactory用于设定XML流和解析器。代码如下:
public class SaxParserUtils { // public static List<Data> readXML(InputStream inputStream) { try { SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser saxParser = spf.newSAXParser(); ContenHanler handler = new ContenHanler(); saxParser.parse(inputStream, handler); inputStream.close(); return handler.getData(); } catch (Exception e) { e.printStackTrace(); } return null; } }
这是最后一个类,用于调用方法展示数据,用Listview展示
public class MainActivity extends AppCompatActivity { InputStream inputStream; ListView lv; private List<Data> personList; private String[] str; String name; String result; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv = (ListView) findViewById(R.id.lv_demoe); try { inputStream = getAssets().open("data.xml"); } catch (IOException e) { e.printStackTrace(); } initData(); lv.setAdapter(new ArrayAdapter<Data>(this, android.R.layout.simple_list_item_1, personList)); } private void initData() { if (inputStream == null) { result = "inputStream is null"; } else { personList = SaxParserUtils.readXML(inputStream); } } }
当我们看下面第二张图片时,不要惊慌,因为没在Data类加toString()方法造成的。加上即可
时间不早了,该睡觉了。