如何使用Android进行简单的XML解析器

问题描述 投票:0回答:6

在那里,我现在已经坚持了一点。我知道它应该很简单,但我似乎无法找到我出错的地方。我在跟踪并尝试调整DOM Parser示例之后构建了我的小XML解析器:http://www.ibm.com/developerworks/opensource/library/x-android/index.html我让它识别节点,但是对于我的生活,我无法弄清楚为什么它告诉我节点的价值是“空值”。非常感谢帮助。

我的XML测试文件。

<?xml version="1.0"?>
<Person>
    <Name>Scott</Name>
    <Gender>Male</Gender>
    <More>And So On..</More>
</Person>

我的Parser代码是。

public class XMLParser {
    InputStream xmlDocument;
    TextView tv;

    public XMLParser(InputStream xmlDocument, TextView tv) {
        this.xmlDocument = xmlDocument;
        this.tv = tv;
    }

    public HashMap<String, String> parse() {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        HashMap<String, String> xmlItems = new HashMap<String, String>();
        try {
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document dom = builder.parse(xmlDocument);
            Element root = dom.getDocumentElement();
            NodeList items = root.getElementsByTagName("Person");
            Element rootElement = (Element)items.item(0);
            items = rootElement.getChildNodes();
            tv.append("\nParser, # of Items: " + String.valueOf(items.getLength()));
            for (int i = 0; i < items.getLength(); i++){
                Node item = items.item(i);
                xmlItems.put(item.getNodeName(), item.getNodeValue());
                tv.append("\nNM: " + item.getNodeName() + " NV: " + item.getNodeValue());
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } 
        return xmlItems;
    }
}
android xml dom
6个回答
1
投票

我正在使用XmlPullFactory,它并没有那么糟糕。

编辑以转换为Hashmap

请注意,这不是真的推荐。此代码不检查hashmap中的重复键,它将覆盖任何现有键!

public HashMap<String, String> parseXml(String xml) {
    XmlPullParserFactory factory;
    String tagName = "";
    String text = "";
            HashMap<String, String> hm = new HashMap<String, String>();

    try {
        factory = XmlPullParserFactory.newInstance();
        factory.setNamespaceAware(true);
        XmlPullParser xpp = factory.newPullParser();
        StringReader sr = new StringReader(xml);
        xpp.setInput(sr);
        int eventType = xpp.getEventType();

        while (eventType != XmlPullParser.END_DOCUMENT) {
            if (eventType == XmlPullParser.TEXT) {
                text = xpp.getText(); //Pulling out node text
            } else if (eventType == XmlPullParser.END_TAG) {
                tagName = xpp.getName();

                                    hm.put(tagName, text);

                text = ""; //Reset text for the next node
            }
            eventType = xpp.next();
        }
    }  catch (XmlPullParserException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (Exception e) {
        Log.d("Exception attribute", e + "+" + tagName);
    }
}

0
投票

看起来Person实际上是这里的根节点,也许你不需要root.getElementsByTagName("Person");

如果您计划拥有多个人可能会将xml文件更改为,然后将其更改为root.getElementsByTagName("Person");


0
投票

0
投票

可以使用SAX解析器,例如:

private void parse(String xml) {
        final ArrayList<Person> people = new ArrayList<Person>();
        Xml.parse(xml, new DefaultHandler() {
            private Person person;
            private StringBuilder builder;

            @Override
            public void startElement(String uri, String localName,
                    String qName, Attributes attributes) throws SAXException {
                builder = new StringBuilder();
                if(localName.equals("Person")) {
                    person = new Person();
                }
            }

            @Override
            public void endElement(String uri, String localName, String qName)
                    throws SAXException {
                if(localName.equals("Person")) {
                    people.add(person);
                }
                else if(localName.equals("Name")){
                    person.setName(builder.toString());
                }
                else if(localName.equals...) {
                    ... etc
                }
            }

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

0
投票

我发现IBM的例子很笨拙而且很乱。我编写了自己的东西来处理RSS提要,它可以适应自定义XML提要。

使用这个的一个例子:

this yahoo feed的内容保存到文件中并将其放在项目中。将文件读入字符串。

String fileContents = ...;
XMLFeed feed = XMLUtils.getXmlFeed(fileContents);

您现在有一个对象,其中包含RSS源中每个条目的列表

下面有4个班级。我为自己的利益评论了一些,但可能会让其他人感到困惑。

基本上,DefaultHandler通过XML字符串查找常见的RSS名称,例如描述,URL,标题等。它将每个条目保存到自己的对象中并将其添加到主列表中。可以更改DefaultHandler类中的常量(最终)字段(添加/删除字符串)以适合您的结构 - 尽管您可能还需要更改XmlFeedItem类的结构。

您应该能够使用它而不更改标准RSS源。

希望能帮助到你

public class XMLUtils {
    public static XmlFeed getXmlFeed(String xmlString) {
        XMLHandler handler = null;
        try {
            XMLReader xr = SAXParserFactory.newInstance().newSAXParser().getXMLReader();

            handler = new XMLHandler();
            xr.setContentHandler(handler);

            InputSource is = new InputSource();
            is.setEncoding("UTF-8");
            is.setCharacterStream(new StringReader(xmlString));

            xr.parse(is);
        }
        catch(SAXException e) {
            return null;
        }
        catch(ParserConfigurationException e) {
            return null;
        }
        catch(IOException e) {
            return null;
        }
        return handler.getXmlFeed();
    }
}

public class XMLHandler extends DefaultHandler {
    /**
     * entity names in the XML document such as <item> which contain many fields
     */
    private static final String OBJECTS[] = new String[] {"item", "entry"};

    /**
     * field names which correspond to a "description"
     */
    private static final String DESCRIPTIONS[] = new String[] {"description", "summary"};

    /**
     * field names which correspond to a "url"
     */
    private static final String URLS[] = new String[] {"link", "id", "guid"};

    /**
     * field names which correspond to "date"
     */
    private static final String PUB_DATES[] = new String[] {"pubDate", "date", "updated", "published", "timestamp"};

    /**
     * field names which correspond to "title"
     */
    private static final String TITLES[] = new String[] {"title"};

    /**
     * the current element being read in
     */
    private String currentElement;
    private boolean foundItem;
    private XmlFeed xmlFeed;
    private XmlFeedItem xmlFeedItem;

    private String object, description, url, pubDate, title;
    public XMLHandler() {
        currentElement = "";
        object = description = url = pubDate = title = null;
        foundItem = false;
        xmlFeed = new XmlFeed();
    }
    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        super.characters(ch, start, length);
        String s = new String(ch, start, length);
        if(foundItem && s.trim().length() > 0) {
            if(isFieldAvailable(currentElement, DESCRIPTIONS, description)) {
                xmlFeedItem.setDescription(xmlFeedItem.getDescription() + s);
            }
            else if(isFieldAvailable(currentElement, URLS, url)) {
                xmlFeedItem.setUrl(xmlFeedItem.getUrl() + s);
            }
            else if(isFieldAvailable(currentElement, PUB_DATES, pubDate)) {
                xmlFeedItem.setPubDate(xmlFeedItem.getPubDate() + s);
            }
            else if(isFieldAvailable(currentElement, TITLES, title)) {
                xmlFeedItem.setTitle(xmlFeedItem.getTitle() + s);
            }
        }
    }
    @Override
    public void endDocument() throws SAXException {
        super.endDocument();
    }
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        super.endElement(uri, localName, qName);
        if(isFieldAvailable(localName, OBJECTS, object)) {
            xmlFeed.getItems().add(new XmlFeedItem(xmlFeedItem));
            xmlFeedItem = new XmlFeedItem();
        }
    }
    @Override
    public void startDocument() throws SAXException {
        super.startDocument();
    }
    /**
     * @param fieldToTest the current element found in the XML string while parsing
     * @param options the array of elements available to match fieldToTest to
     * @param currentField the element that we're currently inside
     * @return <p>if <strong>fieldToTest</strong> is contained in <strong>options</strong> and if <strong>currentField</strong> 
     * is either null or contained in <strong>options</strong>.  This allows us to specify a number of different 
     * fields which mean the same thing in an XML feed.  Example: <strong>summary</strong> may not be included 
     * in a feed but <strong>description</strong> is.  Both <strong>summary</strong> and <strong>description</strong> are contained 
     * in the available <strong>options</strong>, so it is still matched up and used.  Once one element is used 
     * and is contained in <strong>options</strong> it will always use the same element.  <strong>currentField</strong> 
     * is assigned to <strong>fieldToTest</strong> if returning true and if its null(hasn't been matched before)</p>
     */
    private boolean isFieldAvailable(String fieldToTest, String[] options, String currentField) {
        for(String field: options) {
            if(field.equalsIgnoreCase(fieldToTest) && (currentField == null || currentField.equalsIgnoreCase(field))) {
                if(currentField == null) {
                    currentField = new String(fieldToTest);
                }
                return true;
            }
        }
        return false;
    }
    @Override
    public void startElement(String uri, String localName, String qName,
            Attributes attributes) throws SAXException {
        super.startElement(uri, localName, qName, attributes);
        currentElement = new String(localName);
        if(!foundItem && isFieldAvailable(localName, OBJECTS, object)) {
            foundItem = true;
            xmlFeedItem = new XmlFeedItem();
        }
    }
    public XmlFeed getXmlFeed() {
        return xmlFeed;
    }
}

public class XmlFeed {
    private List<XmlFeedItem> items;
    public XmlFeed() {
        items = new ArrayList<XmlFeedItem>();
    }
    public List<XmlFeedItem> getItems() {
        return items;
    }
    public void setItems(List<XmlFeedItem> items) {
        this.items = items;
    }
}

public class XmlFeedItem {
    private String title;
    private String description;
    private String pubDate;
    private String url;
    public XmlFeedItem() {
        title = description = pubDate = url = "";
    }
    public XmlFeedItem(XmlFeedItem rssFeedItem) {
        this.title = rssFeedItem.getTitle();
        this.description = rssFeedItem.getDescription();
        this.pubDate = rssFeedItem.getPubDate();
        this.url = rssFeedItem.getUrl();
    }
    public String getPubDate() {
        return pubDate;
    }
    public void setPubDate(String pubDate) {
        this.pubDate = pubDate;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
}

0
投票

由于API非常低级且难以直接使用,因此Stax / pull非常笨重。请尝试Konsume-XML

val file = File("person.xml")
file.konsumeXml().use { k ->
    k.child("Person") {
        println(childText("Name"))
        println(childText("Gender"))
        println(childText("More"))
    }
}

这将打印

Scott
Male
And So On..
© www.soinside.com 2019 - 2024. All rights reserved.