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

时间:2011-11-09 20:09:21

标签: android xml dom

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

我的XML测试文件。

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

My 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;
    }
}

7 个答案:

答案 0 :(得分: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);
    }
}

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

也许从IBM示例转移到像这样的简单示例: http://p-xr.com/android-tutorial-how-to-parseread-xml-data-into-android-listview/

答案 3 :(得分: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;
    }
}

答案 4 :(得分: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);
            }
        });
    }

答案 5 :(得分:0)

Stax / pull非常笨拙,因为API相当底层并且很难直接使用。请改为尝试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..

答案 6 :(得分:0)

我分享是因为我没有找到关于此答案的好的代码,也不会在项目中添加其他外部库,所以我做了一个简单的XML解析器,它可以转换XML->列表 显然,通过使用这种概念,任何人都可以比这种大声笑更好地实现

我在PhP中做了另一个XML解析器,该解析器仅使用对象(如果有人只需要在上面写注释,我会分享它。php对于在运行中创建自定义对象,php更为自由。)

XML解析递归函数,以解析完整修剪的XML字符串(XML标记之间没有空格,并且\ n和\ t之间没有空格),并获取HashMaps树列表(HashMap树是HashMap,其中可以包含其他HashMaps,该HashMaps可以包含其他HashMaps ecc ecc, 所以在代码中: HashMap,其中T可以是HashMap或例如String 所以 哈希图>>

用法:

DataUtils.parseStringXML(XML.replaceAll("[\\n\\t ]*", "")));

库代码:

(#LoL编辑于10/07/2019 18:00->添加了“ lastEvent”以管理空的XML标签)

(#LoL再次编辑10/07/2019 18:52->固定“ lastEvent”以管理空的XML标签)

/** XML Parsing Methods **/
public static <T extends Object> List<HashMap<String, T>> parseStringXML(String xml){
    List<HashMap<String, T>> ret = new ArrayList<>();
    if(xml != null && !TextUtils.isEmpty(xml)){
        try{
            XmlPullParserFactory xppFactory = XmlPullParserFactory.newInstance();
            xppFactory.setNamespaceAware(true);
            XmlPullParser xpp = xppFactory.newPullParser();
            xpp.setInput(new StringReader(xml));
            ret = parseTagsXML(xpp);
        } catch (XmlPullParserException xppE){
            EMaxLogger.onException(TAG, xppE);
        } catch (Exception e){
            EMaxLogger.onException(TAG, e);
        }
    }
    return ret;
}

/** XML Parsing Methods **/
private static <T extends Object> T parseTagsXML(XmlPullParser xpp) {
    int index = 0x0;
    List<HashMap<String, T>> tree = new ArrayList<HashMap<String, T>>(){{add(new HashMap<>());}};
    try{
        List<String> tags = new ArrayList<>();
        int event = 0x0; int lastEvent;
        while(event != XmlPullParser.END_DOCUMENT){
            lastEvent = xpp.getEventType();
            if(lastEvent == XmlPullParser.END_TAG && tags.contains(xpp.getName())){
                tags.remove(xpp.getName());
                if(tags.size() == 0x0){
                    return (T) new HashMap<String, T>(){{put(xpp.getName(), null);}};
                }
            }
            event = xpp.next();
            switch (event){
                case XmlPullParser.START_TAG:
                    tags.add(xpp.getName());
                    if(tags.size() >= 0x2 && containsStringKeyInMapsTree(tree.get(index), tags.get(tags.size() - 0x2))){
                        tree.set(index, putMapElementInTreeMaps(tags.get(tags.size() - 0x2), tree.get(index), tags.get(tags.size() - 0x1), parseTagsXML(xpp)));
                    } else {
                        tree.get(index).put(tags.get(tags.size() - 0x1), parseTagsXML(xpp));
                    }
                    break;
                case XmlPullParser.TEXT:
                    return (T) xpp.getText();
                case XmlPullParser.END_TAG:
                    if(tags.size() > 0x0 && tags.contains(xpp.getName())) {
                        tags.remove(xpp.getName());
                        if(tags.size() == 0x0){
                            if(xpp.getDepth() == 0x1) {
                                index++;
                                tree.add(new HashMap<>());
                                break;
                            } else {
                                return (T) tree.get(index);
                            }
                        }
                    }
                    if(lastEvent == XmlPullParser.START_TAG){
                        return null;
                    }
                    break;
            }
        }
        if(tree.size() >= index && (tree.get(index) == null || tree.get(index).isEmpty())) {
            tree.remove(index);
        }
    } catch(IOException ioE){
        EMaxLogger.onException(TAG, ioE);
    } catch(XmlPullParserException xppE){
        EMaxLogger.onException(TAG, xppE);
    }
    return (T) tree;
}

/** Tree HashMap Methods **/
private static <T extends Object> boolean containsStringKeyInMapsTree(HashMap<String, T> tree, String key) {
    if(tree != null){
        if(tree.containsKey(key)){
            return true;
        } else if(tree.size() > 0x0){
            for(String k : tree.keySet()){
                if(k != null && !TextUtils.isEmpty(k.trim()) && tree.get(k) != null && tree.get(k) instanceof HashMap && containsStringKeyInMapsTree((HashMap<String, T>) tree.get(k), key)){
                    return true;
                }
            }
        }
    }
    return false;
}

private static <T extends Object> HashMap<String, T> putMapElementInTreeMaps(String parentKey, HashMap<String, T> tree, String elementKey, T element){
    if(tree != null){
        if(tree.containsKey(parentKey) && tree.get(parentKey) != null && tree.get(parentKey) instanceof HashMap){
            ((HashMap<String, T>) tree.get(parentKey)).put(elementKey, element);
        } else if(tree.size() > 0x0){
            for(String key : tree.keySet()){
                if(key != null && !TextUtils.isEmpty(key.trim()) && tree.get(key) != null && tree.get(key) instanceof HashMap){
                    tree.put(key, (T) putMapElementInTreeMaps(parentKey, (HashMap<String, T>) tree.get(key), elementKey, element));
                }

            }
        }
    }
    return tree;
}

它使用递归。 也许我需要做一个XML解析器,它将XML转换为通用对象。 为此,您需要在对象中使用“预先确定”的“ setter和getter”方法。 例如,对于每个标记XML,您将具有“ getTAG_NAME()”和“ setTAG_NAME”方法,可用于在对象内部设置值。

为此,您需要使用Java-Field和Method类,例如,使用名称设置Object字段:

public static void setFieldValue(Object obj, Field field, int value){
    try {
        field.setInt(obj, value);
    } catch (IllegalAccessException iacE) {
        EMaxLogger.onException(TAG, iacE);
    } catch (IllegalArgumentException iarE) {
        EMaxLogger.onException(TAG, iarE);
    } catch (Exception e) {
        EMaxLogger.onException(TAG, e);
    }
}

因此,每当您有一个新的XML_TAG和一个新的TAG_VALUE时,都可以使用上面的函数调用相应的“ setXML_TAG(TAG_VALUE)”方法。

函数原型如下:

public static <T extends Object> T parseStringXmlToGenericObject(T myObj, String xml){ [...] }

其中T是用于存储XML TAG值的特定对象,例如:

 <root_xml_tag>
      <xml_tag_0x1>lol</xml_tag_0x1> 
      <xml_tag_0x2>
           <xml_tag_0x2a>asd</xml_tag_0x2a>
           <xml_ag_0x2b>lmao</xml_tag_0x2b>
      </xml_tag_0x2>
      <xml_tag_0x3>rotfl</xml_tag_0x3>
 </root_xml_tag>

如果您有xml之类的东西,那么用于存储XML数据的对象将是:

public class RootXmlTag {
  private String mXmlTag0x1;
  private XmlTag0x2 mXmlTag0x2;
  private String mXmlTag0x3;

  /** Setter & Getter Methods **/
  public void setxml_tag_0x1(String val){ 
     mXmlTag0x1 = val;
  }

  public String getxml_tag_0x1(){
     return mXmlTag0x1;
  }

  public void setxml_tag_0x2(XmlTag0x2 val){ 
     mXmlTag0x2 = val;
  }

  public XmlTag0x2 getxml_tag_0x2(){
     return mXmlTag0x2;
  }

  [... Ecc Ecc Setter & Getter Methods 4 Every Objects Properties ...]

  [... Other Methods you will need ....]


  /** Inner Classes For Parents XML Tags (Tags with Tags Sons ecc..) **/
  class XmlTag0x2 {

     private String mXmlTag0x2a;
     private String mXmlTag0x2b;

     /** Getter & Setter Methods **/
     [... See Outer Parent Class ...]
  }

}

因此,当您获得xml标记时,您就可以做一个非常简单的示例(概念验证[PoC]:

// Assume having "T myObj" where T = RootXmlTag. 

String xmlTagName = xpp.getName();
event = xpp.next();
String tagValue = xpp.getText();
Utilities.setFieldValue(yourObj, "set".concat(xmlTagName), tagValue);

显然,要做到这一点,您需要在解析XML之前先知道XML的样子(当您不知道它时呢?大声笑。显然,您会知道它的样子,也许您会使用不同的XML版本标记,但您将永远知道所得到的结构。)

我个人实际上实际上是使用HashMaps Trees的方式,只是因为我不想创建很多仅用于解析2或3个XML的类,所以实际上我没有实现对象方式。如果愿意,我会分享。

GG 再见,编码不错!

(我想如果有人使用递归函数发布其他解决方案,只是为了比较和学习更多方法(:谢谢!再见)