android 自学日记(六) ——SAX解析中换行问题解决

今天在写一个小项目的时候用到了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> 后才换行,达到我们想要的换行方式。

你可能感兴趣的:(android,xml,sax)