开发者

Android XML parsing with different element names

I need help :D. I am using web service that returns similar xml file to the one below:

<books>
<item_1>
    <name></name>
    <type></type>
</item_1>
<item_2>
    <name></name>
    <type></type>
</item_2>
<item_3>
    <name></name>
    <type></type>
</item_3>
...
...</books>

The problem is that I don't know how to parse it in An开发者_JS百科droid. Everything would be fine if the child elements of the 'books' tag would have the same name (item instead of: item_1, item_2, item_3...) Then I could easily use SAX parser.

Do you know how I could pare this kind of document in Android?

Thanks, marqs

EDITED:

My problem is that the child elements of <<>books> tag ale numbered (item1,item2,item3 and so on) so I cannot identify them. That would not be a problem if the 'books' tag would have only 'item' child - so then I could use SAX parser and fetch 'item' list


The problem that you've described doesn't sound all that hard: If you can safely assume that every child of the books element is an "item", then you can invoke your "item handler" for each child.

Here's pseudocode that (I think) would work, assuming that <books> is always and only the top-level element of the document:

class BookHandler extends DefaultHandler {
   public void startElement(String namespaceURI, String localName, 
                String qName, Attributes atts) {
        if (localName.equals("books") {
            // Don't need to do anything with the top level element
            return;
        }
        else {
            handleItem(namespaceURI, localName, qName, atts);
        }
    }

    public void  endElement(String namespaceURI, String localName, String qName) {
         if localName.equals("books") {
            // Stop parsing and exit.
         }
         else {
            // Stop parsing one item
         }
    }

    private void handleItem(String namespaceURI, String localName, 
                 String qName, Attributes atts) {
         // Handle one item, including handling the "name" and 
         // "type" child attributes
         if (localName.equals("name") {
            // handle the name
         }
         else if (localName.equals("type") {
            // handle the type
         }
    }
}

Even as pseudocode, that's over-simplistic and ugly. An alternative approach, but which may be overblown for your needs, is to decompose your application into multiple ContentHandler classes, passing off responsibility as you reach the beginning or ending of certain elements.

For example: Let's assume that an instance of BookHandler is passed in to the parser.parse() call, to handle the top-level elements. Then:

class BookHandler extends DefaultHandler {
   private ContentHandler m_parentHandler;
   private ContentHandler m_childHandler = null;

   // ... == appropriate args for the DefaultHandler constructor
   public BookHandler(ContentHandler parent, Attributes atts, ...) {
        super(...);
        m_parentHandler = parent;
        parent.getXMLReader().setHandler(this);
   }

   public void startElement(String namespaceURI, String localName, 
                String qName, Attributes atts) {
        if (localName.equals("books") {
            // Don't need to do anything with the top level element
            return;
        }
        else {
            // Assume it's a new item element. (Note: ItemHandler's constructor
            // changes the parser's ContentHandler.)
            m_childHandler = new ItemHandler(this, atts, ...);
        }
    }

    public void  endElement(String namespaceURI, String localName, String qName) {
         if localName.equals("books") {
            // Stop parsing and exit.
         }
         else {
            // Note that this won't be called for "item" elements, UNLESS the
            // ItemHandler's endElement method explicitly invokes this method.

            // Stop parsing one item

         }
    }    
}


class ItemHandler extends DefaultHandler {
   private ContentHandler m_parentHandler;

    // ItemInfo is a class that holds all info about the current item
   private ItemInfo m_ItemInfo = null;

   // ... == appropriate args for the DefaultHandler constructor
   public ItemHandler(ContentHandler parent, Attributes atts, ...) {
        super(...);
        m_parentHandler = parent;
        m_ItemInfo = new ItemInfo(atts);
        parent.getXMLReader().setHandler(this);
   }

   public void startElement(String namespaceURI, String localName, 
                String qName, Attributes atts) {
        if (localName.equals("name") {
             // Handle the name. Update the current item as needed.
        }
        else  if (localName.equals("type") {
             // Handle the type. Update the current item as needed.
        }
    }

    public void  endElement(String namespaceURI, String localName, String qName) {
         if localName.equals("name" || localName.equals("type") {
             // Do nothing (?)
         }
         else {
            // Stop parsing the current item; 
            // let the parent class handle the next element.
            getXMLReader().setHandler(m_parentHandler);

            // OPTIONALLY -- depending on your app's needs -- call the
            // parent's endElement() method to let it know that we reached
            // the end of an item.
            m_parentHandler.endElement(namespaceURI, localName, qName);
         }
    }   
}

This scheme offers more flexibility, and more possibilities for reuse. For example, potentially the "books" element could now be a child of some other element, say "departments", without requiring much if any changes to these classes.

The pseudo-code isn't perfect by any means. (For one thing, I'd want to think some more about where the handlers are switched off -- in the parent class, or in the child. For another thing, I may have gone overboard in saving references to both parents and children in these classes.) But I hope it gives you ideas that you can start working with.


I've previously used DocumentBuilderFactory

URL connectURL;
InputStream is;
NodeList names;
NodeList types

try
{
  connectURL = new URL("http://example.com/exampleservice");

  try
  {
    URLConnection connection = connectURL.openConnection();
    is = connection.getInputStream();
  } catch (IOException e)
  {
    e.printStackTrace();
  }
} catch (MalformedURLException e1)
{
  e1.printStackTrace();
}

DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
try
{
  DocumentBuilder docBuillder = docBuilderFactory.newDocumentBuilder();
  Document doc = docBuillder.parse(is);
  names = doc.getElementsByTagName("name");
  types = doc.getElementsByTagName("type");
  } catch (ParserConfigurationException e)
  {
    e.printStackTrace();
  } catch (SAXException e)
  {
    e.printStackTrace();
  } catch (IOException e)
  {
    e.printStackTrace();
  }
}

That should produce NodeLists which should contain the stuff you want to play around with.

Thanks,

ElDog


That seems like a very odd XML format, impossible to write a schema for. I assume you don't have control over it? It would make a lot more sense to do something like .

If you are using a dom parser, you can walk the tree of elements without knowing what the names of the elements are. From your books element, just call getChildNodes.

With a SAX parser, in your handler you just need to look for an element name that contains item as opposed to be exactly equal to item. Something like:

 public void startElement(String uri, String localName, String name,
            Attributes attributes) throws SAXException {
        super.startElement(uri, localName, name, attributes);
        if (localName.contains("item")){
            // create some item object
        }
    }
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜