今天在写一个小项目的时候用到了SAX解析,遇到了一点小问题,网上找了好久都没有解决,最后还是自己发现了解决方法,特地和大家分享一下!

问题描述:

先来看下要解析XML文件:

<dict num="219" id="219" name="219"><key>big</key><ps>bɪg</ps><pron>http://res.iciba.com/resource/amp3/oxford/0/dd/3e/dd3eded85f7085dc92210aa496e92ba9.mp3</pron><ps>bɪɡ</ps><pron>http://res.iciba.com/resource/amp3/1/0/d8/61/d861877da56b8b4ceb35c8cbfdf65bb4.mp3</pron><pos>adj.</pos><acceptation>大的;重要的;(计划)庞大的;大方的;</acceptation><pos>adv.</pos><acceptation>大量地;成功地;夸大地;宽宏大量地;</acceptation><pos>n.</pos><acceptation>大亨;大公司;</acceptation><sent><orig>The word " big " has one syllable and the word " Africa " has three syllables.</orig><trans>“ big ” 这个词只有一个音节而 “ Africa ” 有三个音节.</trans></sent><sent><orig>There remains a tension at the heart of BIG s predicament.</orig><trans>目前,BIG心里仍维持着一种紧张的态势.</trans></sent><sent><orig>BIG might sell its Bsian arm to another insurance firm; Prudential was not the only bidder.</orig><trans>BIG也许会将其亚洲分支出售给另外一家保险公司, 保信并不是独一的竞价人.</trans></sent><sent><orig>Carrie fell for the wealthy Mr. Big.</orig><trans>Carrie爱上了那位富有的Big先生.</trans></sent><sent><orig>In this sentence, the word BIG is in capitals.</orig><trans>本句中BIG一词用的是大写字母.</trans></sent></dict>

大家都知道SAX解析的关键就是setContentHandler()方法中传入的DefaultHandler的子类,一开始我是这样写的:

public class WordsHandler extends DefaultHandler {    //记录当前节点    private String nodeName;    private Words words;    //单词的词性与词义    private StringBuilder posAcceptation;    //例句    private StringBuilder sent;    /**获取解析后的words对象*/    public Words getWords() {        return words;    }    //开始解析XML时调用    @Override    public void startDocument() throws SAXException {        //初始化        words = new Words();        posAcceptation = new StringBuilder();        sent = new StringBuilder();    }    //结束解析XML时调用    @Override    public void endDocument() throws SAXException {        //将所有解析出来的内容赋予words        words.setPosAcceptation(posAcceptation.toString().trim());        words.setSent(sent.toString().trim());    }    //开始解析节点时调用    @Override    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {        nodeName = localName;    }    //结束解析节点时调用    @Override    public void endElement(String uri, String localName, String qName) throws SAXException {    }    //获取节点中内容时调用    @Override    public void characters(char[] ch, int start, int length) throws SAXException {        String a = new String(ch, start, length);        //去掉文本中原有的换行        for (int i = start; i < start + length; i++) {            if (ch[i] == '\n')                return;        }        //将节点的内容存入Words对象对应的属性中        if ("key".equals(nodeName)) {            words.setKey(a);        } else if ("ps".equals(nodeName)) {            if (words.getPsE().length() <= 0) {                words.setPsE(a);            } else {                words.setPsA(a);            }        } else if ("pron".equals(nodeName)) {            if (words.getPronE().length() <= 0) {                words.setPronE(a);            } else {                words.setPronA(a);            }        } else if ("pos".equals(nodeName)) {            posAcceptation.append(a);        } else if ("acceptation".equals(nodeName)) {            posAcceptation.append(a);             posAcceptation.append("\n");//重新排版换行        } else if ("orig".equals(nodeName)) {            sent.append(a);            sent.append("\n");        } else if ("trans".equals(nodeName)) {            sent.append(a);            sent.append("\n");        } else if ("fy".equals(nodeName)) {            words.setFy(a);            words.setIsChinese(true);        }    }}

这样的代码基本没什么问题,运行一下,大部分情况都能按照想要结果返回。
但是那,如果解析的字段中出现了“‘ < >这样的特殊符号时,就会发生问题 ,例如这句:

The word " big " has one syllable and the word " Africa " has three syllables.

运行后会发现解析结果是这样的:

The word " big " has one syllable and the word" Africa " has three syllables.

和想要的结果不一样,于是我找了好多资料,终于在理解的情况下解决了。

解决方法:

characters()方法在解析的时候会不停地调用,每次读取都是以 ’ ” < > 等这些字段为节点,上述情况就是因为characters()方法一次只读到文本中 ” 位置,并没有读完整个节点,因此添加了换行,而后再次调用characters()方法,读取的是“之后的内容,还是在这个节点中。正是这种同一个节点中不同地调用characters()方法,导致产生很多不需要的换行。

修改WordsHandler:

public class WordsHandler extends DefaultHandler {    //记录当前节点    private String nodeName;    private Words words;    //单词的词性与词义    private StringBuilder posAcceptation;    //例句    private StringBuilder sent;    /**获取解析后的words对象*/    public Words getWords() {        return words;    }    //开始解析XML时调用    @Override    public void startDocument() throws SAXException {        //初始化        words = new Words();        posAcceptation = new StringBuilder();        sent = new StringBuilder();    }    //结束解析XML时调用    @Override    public void endDocument() throws SAXException {        //将所有解析出来的内容赋予words        words.setPosAcceptation(posAcceptation.toString().trim());        words.setSent(sent.toString().trim());    }    //开始解析节点时调用    @Override    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {        nodeName = localName;    }    //结束解析节点时调用    @Override    public void endElement(String uri, String localName, String qName) throws SAXException {        //在读完整个节点后换行        if("acceptation".equals(localName)){            posAcceptation.append("\n");        }else if("orig".equals(localName)){            sent.append("\n");        }else if("trans".equals(localName)){            sent.append("\n");            sent.append("\n");        }    }    //获取节点中内容时调用    @Override    public void characters(char[] ch, int start, int length) throws SAXException {        String a = new String(ch, start, length);        //去掉文本中原有的换行        for (int i = start; i < start + length; i++) {            if (ch[i] == '\n')                return;        }        //将节点的内容存入Words对象对应的属性中        if ("key".equals(nodeName)) {            words.setKey(a);        } else if ("ps".equals(nodeName)) {            if (words.getPsE().length() <= 0) {                words.setPsE(a);            } else {                words.setPsA(a);            }        } else if ("pron".equals(nodeName)) {            if (words.getPronE().length() <= 0) {                words.setPronE(a);            } else {                words.setPronA(a);            }        } else if ("pos".equals(nodeName)) {            posAcceptation.append(a);        } else if ("acceptation".equals(nodeName)) {            posAcceptation.append(a);        } else if ("orig".equals(nodeName)) {            sent.append(a);        } else if ("trans".equals(nodeName)) {            sent.append(a);        } else if ("fy".equals(nodeName)) {            words.setFy(a);            words.setIsChinese(true);        }    }}

仅仅是把重新排版的过程放到了endElement()方法执行,此方法是在节点结束时调用,即读到 </xxx> 这个标志时, 在这个方法中添加我们想要的换行就可以了。
上述讲到的那句:The word ” big ” has one syllable and the word ” Africa ” has three syllables.是在一个<org> </org> 中的,因此也只会在读取到 </org> 后才换行,达到我们想要的换行方式。

更多相关文章

  1. SpringBoot 2.0 中 HikariCP 数据库连接池原理解析
  2. Android(安卓)ApiDemos示例解析(127):Views->Layout Animation->
  3. Android(安卓)AsyncTask源码解析
  4. Android(安卓)Manifest.xml文件解析
  5. 三种解析xml的方式
  6. Android(安卓)约束布局ConstraintLayout解析
  7. Android(安卓)布局优化之include与merge,最后有ViewStub
  8. Android(安卓)AsyncTask 详细解析
  9. Android多线程(二)消息处理机制---Handler、Message、Looper源码原

随机推荐

  1. Android中修改系统时间的几种方式
  2. Android之DiskLruCache(缓存工具)
  3. Android客户端向服务器端传值——登录实
  4. android开发随记 - Google Map
  5. windows下Qt5.1 for android开发环境配置
  6. 使用PC端chrome 浏览器远程连接android设
  7. 谁调用了activity的oncreate()方法
  8. [转]Android SDK升级时的错误及解决方法
  9. 告别Dagger2模板代码:DaggerAndroid使用详
  10. Android display架构分析(二)