如何使用SAX解析器解析XML

Joh*_*han 58 java xml parsing android sax

我正在学习本教程.

它工作得很好,但我希望它返回一个包含所有字符串的数组,而不是带有最后一个元素的单个字符串.

任何想法如何做到这一点?

Oct*_*ean 179

因此,您希望构建一个XML解析器来解析像这样的RSS提要.

<rss version="0.92">
<channel>
    <title>MyTitle</title>
    <link>http://myurl.com</link>
    <description>MyDescription</description>
    <lastBuildDate>SomeDate</lastBuildDate>
    <docs>http://someurl.com</docs>
    <language>SomeLanguage</language>

    <item>
        <title>TitleOne</title>
        <description><![CDATA[Some text.]]></description>
        <link>http://linktoarticle.com</link>
    </item>

    <item>
        <title>TitleTwo</title>
        <description><![CDATA[Some other text.]]></description>
        <link>http://linktoanotherarticle.com</link>
    </item>

</channel>
</rss>
Run Code Online (Sandbox Code Playgroud)

现在您可以使用两个SAX实现.您可以使用org.xml.saxandroid.sax实施.在发布一个简短的案例之后,我将解释两者的专业和概念.

android.sax实现

让我们从android.sax实施开始.

首先必须使用RootElementElement对象定义XML结构.

在任何情况下,我都会使用POJO(Plain Old Java Objects)来保存您的数据.这将是所需的POJO.

Channel.java

public class Channel implements Serializable {

    private Items items;
    private String title;
    private String link;
    private String description;
    private String lastBuildDate;
    private String docs;
    private String language;

    public Channel() {
        setItems(null);
        setTitle(null);
        // set every field to null in the constructor
    }

    public void setItems(Items items) {
        this.items = items;
    }

    public Items getItems() {
        return items;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getTitle() {
        return title;
    }
    // rest of the class looks similar so just setters and getters
}
Run Code Online (Sandbox Code Playgroud)

该类实现了Serializable接口,因此您可以将其放入Bundle并使用它执行某些操作.

现在我们需要一个班来保存我们的物品.在这种情况下,我只是要扩展ArrayList课程.

Items.java

public class Items extends ArrayList<Item> {

    public Items() {
        super();
    }

}
Run Code Online (Sandbox Code Playgroud)

这是我们的物品容器.我们现在需要一个类来保存每个项目的数据.

Item.java

public class Item implements Serializable {

    private String title;
    private String description;
    private String link;

    public Item() {
        setTitle(null);
        setDescription(null);
        setLink(null);
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getTitle() {
        return title;
    }

    // same as above.

}
Run Code Online (Sandbox Code Playgroud)

例:

public class Example extends DefaultHandler {

    private Channel channel;
    private Items items;
    private Item item;

    public Example() {
        items = new Items();
    }

    public Channel parse(InputStream is) {
        RootElement root = new RootElement("rss");
        Element chanElement = root.getChild("channel");
        Element chanTitle = chanElement.getChild("title");
        Element chanLink = chanElement.getChild("link");
        Element chanDescription = chanElement.getChild("description");
        Element chanLastBuildDate = chanElement.getChild("lastBuildDate");
        Element chanDocs = chanElement.getChild("docs");
        Element chanLanguage = chanElement.getChild("language");

        Element chanItem = chanElement.getChild("item");
        Element itemTitle = chanItem.getChild("title");
        Element itemDescription = chanItem.getChild("description");
        Element itemLink = chanItem.getChild("link");

        chanElement.setStartElementListener(new StartElementListener() {
            public void start(Attributes attributes) {
                channel = new Channel();
            }
        });

        // Listen for the end of a text element and set the text as our
        // channel's title.
        chanTitle.setEndTextElementListener(new EndTextElementListener() {
            public void end(String body) {
                channel.setTitle(body);
            }
        });

        // Same thing happens for the other elements of channel ex.

        // On every <item> tag occurrence we create a new Item object.
        chanItem.setStartElementListener(new StartElementListener() {
            public void start(Attributes attributes) {
                item = new Item();
            }
        });

        // On every </item> tag occurrence we add the current Item object
        // to the Items container.
        chanItem.setEndElementListener(new EndElementListener() {
            public void end() {
                items.add(item);
            }
        });

        itemTitle.setEndTextElementListener(new EndTextElementListener() {
            public void end(String body) {
                item.setTitle(body);
            }
        });

        // and so on

        // here we actually parse the InputStream and return the resulting
        // Channel object.
        try {
            Xml.parse(is, Xml.Encoding.UTF_8, root.getContentHandler());
            return channel;
        } catch (SAXException e) {
            // handle the exception
        } catch (IOException e) {
            // handle the exception
        }

        return null;
    }

}
Run Code Online (Sandbox Code Playgroud)

现在,这是一个非常快速的例子,你可以看到.使用android.saxSAX实现的主要优点是,您可以定义必须解析的XML的结构,然后只需将事件侦听器添加到适当的元素.缺点是代码变得非常重复和臃肿.

org.xml.sax实现

org.xml.saxSAX处理程序实现是一个有点不同.

在这里,您不要指定或声明XML结构,而只是监听事件.最广泛使用的是以下事件:

  • 文件开始
  • 文件结束
  • 元素开始
  • 元素结束
  • 元素开始和元素结束之间的字符

使用上面的Channel对象的示例处理程序实现如下所示.

public class ExampleHandler extends DefaultHandler {

    private Channel channel;
    private Items items;
    private Item item;
    private boolean inItem = false;

    private StringBuilder content;

    public ExampleHandler() {
        items = new Items();
        content = new StringBuilder();
    }

    public void startElement(String uri, String localName, String qName, 
            Attributes atts) throws SAXException {
        content = new StringBuilder();
        if(localName.equalsIgnoreCase("channel")) {
            channel = new Channel();
        } else if(localName.equalsIgnoreCase("item")) {
            inItem = true;
            item = new Item();
        }
    }

    public void endElement(String uri, String localName, String qName) 
            throws SAXException {
        if(localName.equalsIgnoreCase("title")) {
            if(inItem) {
                item.setTitle(content.toString());
            } else {
                channel.setTitle(content.toString());
            }
        } else if(localName.equalsIgnoreCase("link")) {
            if(inItem) {
                item.setLink(content.toString());
            } else {
                channel.setLink(content.toString());
            }
        } else if(localName.equalsIgnoreCase("description")) {
            if(inItem) {
                item.setDescription(content.toString());
            } else {
                channel.setDescription(content.toString());
            }
        } else if(localName.equalsIgnoreCase("lastBuildDate")) {
            channel.setLastBuildDate(content.toString());
        } else if(localName.equalsIgnoreCase("docs")) {
            channel.setDocs(content.toString());
        } else if(localName.equalsIgnoreCase("language")) {
            channel.setLanguage(content.toString());
        } else if(localName.equalsIgnoreCase("item")) {
            inItem = false;
            items.add(item);
        } else if(localName.equalsIgnoreCase("channel")) {
            channel.setItems(items);
        }
    }

    public void characters(char[] ch, int start, int length) 
            throws SAXException {
        content.append(ch, start, length);
    }

    public void endDocument() throws SAXException {
        // you can do something here for example send
        // the Channel object somewhere or whatever.
    }

}
Run Code Online (Sandbox Code Playgroud)

现在说实话,我无法真正告诉你这个处理程序实现的真正优势android.sax.但是,我可以告诉你现在应该非常明显的劣势.看一下方法中的else if语句startElement.由于这样的事实,我们有标签<title>,linkdescription我们在XML结构,我们目前跟踪那里.也就是说,如果我们遇到一个<item>起始标记,我们设置inItem标记以true确保我们将正确的数据映射到正确的对象,并且在endElement我们设置该标记的方法中,false如果遇到</item>标记.表示我们已完成该项目标记.

在这个例子中,管理它很容易,但是必须用不同级别的重复标记来解析更复杂的结构变得棘手.在那里你必须使用Enums来设置你当前的状态和很多switch/case statemenet来检查你的位置,或者更优雅的解决方案是使用标签栈的某种标签跟踪器.