ANDROID: Parsing XML
I'm not a developer, I just dabble in programming. One area I never understand is XML parsing. Sadly, for my latest "project" I need to do this very thing for an android app. Its a prototype that I am doing for work.
I have this XML (a mock-up file):
<feed version="201010011221" >
<period from="2010-10-01T10:08:34Z" to="2010-10-01T10:08:34Z">
<lines>
<line id="SKI" name="Ski" shortname="ski" status="1 calls queued">
开发者_StackOverflow社区<calls>
<call id="6584" created="2010-10-01T11:22:42Z">
<booking>1275243</booking>
</call>
</calls>
</line>
<line id="CRU" name="Cruise" shortname="cruise" status="0 calls queued">
<calls />
</line>
<line id="VIL" name="Villas" shortname="villas" status="2 calls queued">
<calls>
<call id="25878" created="2010-10-01T10:22:42Z">
<booking>1077244</booking>
</call>
<call id="25878" created="2010-10-01T10:22:42Z">
<booking>1077244</booking>
</call>
</calls>
</line>
</lines>
</period>
</feed>
I have some code that gets me a NodeList of each :
inputStream = OpenHttpConnection(URL);
Document document = null;
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder;
documentBuilder = documentBuilderFactory.newDocumentBuilder();
document = documentBuilder.parse(inputStream);
document.getDocumentElement().normalize();
NodeList lineNodes = document.getElementsByTagName("line");
I'm not sure what to do next. My code seems quite long for this. I've googled for better methods, but some cleaner code I found, I couldn't get to work.
Are there any good Android XML tutorials out there? Or can someone assist me with this code?
I need to get each into a HashMap where String is the ID.
Line is class of all info shown.
Thanks Neil
I'd n rather not use DOM as it has a bigger memory footprint. With DOM the whole XML structure is first loaded into memory and then it is being processed. This might not be the best solution for mobile development.
Use the SAX parser which comes with Android. It is an event driven approach. Every starting tag, content in between tags and end tag fire events when they occur. Actually it can handle more events but those are the most commonly used events. This means that the SAX parser processes each line one by one without loading the whole XML structure into memory first.
I will post an example for your particular problem tomorrow.
EDIT: Here is the promised content handler example.
import java.util.HashMap;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
public class MyContentHandler implements ContentHandler {
private HashMap<String, Object> feed;
private HashMap<String, Object> peroidContent;
private HashMap<String, Object> callContent;
private HashMap<String, Object> callsMap;
private HashMap<String, Object> lineContent;
private HashMap<String, Object> linesMap;
private String text;
private String callId;
private String lineId;
@Override
public void startDocument() throws SAXException {
/* You can perform some action in this method
* for example to reset some sort of Collection
* or any other variable you want. It gets called
* every time a document starts. */
feed = new HashMap<String, Object>();
}
@Override
public void startElement(String uri, String localName, String qName,
Attributes atts) throws SAXException {
// Gets called every time an opening tag is encountered.
if(localName.equalsIgnoreCase("FEED")) {
/* We've found a "feed" opening tag so we capture its
* version attribute and put it into our HashMap.*/
feed.put("Version", atts.getValue("version"));
} else if(localName.equalsIgnoreCase("PEROID")) {
peroidContent = new HashMap<String, Object>();
peroidContent.put("From", atts.getValue("from"));
peroidContent.put("to", atts.getValue("to"));
} else if(localName.equalsIgnoreCase("LINE")) {
linesMap = new HashMap<String, Object>();
} else if(localName.equalsIgnoreCase("LINE")) {
lineContent = new HashMap<String, Object>();
lineId = atts.getValue("id");
lineContent.put("name", atts.getValue("name"));
lineContent.put("shortname", atts.getValue("shortname"));
lineContent.put("status", atts.getValue("status"));
} else if(localName.equalsIgnoreCase("CALLS")) {
callsMap = new HashMap<String, Object>();
} else if(localName.equalsIgnoreCase("CALL")) {
callContent = new HashMap<String, Object>();
callId = atts.getValue("Id");
callContent.put("created", atts.getValue("created"));
}
}
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
/* Gets called every time in between an opening tag and
* a closing tag if characters are encountered. */
text = new String(ch, start, length);
}
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
// Gets called every time a closing tag is encountered.
if(localName.equalsIgnoreCase("FEED")) {
feed.put("Peroid", peroidContent);
} else if(localName.equalsIgnoreCase("PEROID")) {
peroidContent.put("Lines", linesMap);
} else if(localName.equalsIgnoreCase("LINES")) {
linesMap.put(lineId, lineContent);
} else if(localName.equalsIgnoreCase("LINE")) {
lineContent.put("Calls", callsMap);
} else if(localName.equalsIgnoreCase("CALLS")) {
callsMap.put(callId, callContent);
} else if(localName.equalsIgnoreCase("BOOKING")) {
callContent.put("Booking", text.toString());
}
}
@Override
public void endDocument() throws SAXException {
/* You can perform some action in this method
* for example to reset some sort of Collection
* or any other variable you want. It gets called
* every time a document end is reached. */
SAXParsingFun.setHashMap(feed);
}
@Override
public void endPrefixMapping(String prefix) throws SAXException {
// TODO Auto-generated method stub
}
@Override
public void ignorableWhitespace(char[] ch, int start, int length)
throws SAXException {
// TODO Auto-generated method stub
}
@Override
public void processingInstruction(String target, String data)
throws SAXException {
// TODO Auto-generated method stub
}
@Override
public void setDocumentLocator(Locator locator) {
// TODO Auto-generated method stub
}
@Override
public void skippedEntity(String name) throws SAXException {
// TODO Auto-generated method stub
}
@Override
public void startPrefixMapping(String prefix, String uri)
throws SAXException {
// TODO Auto-generated method stub
}
}
There are a lot of explanations on StackOverflow about how to parse a XML file I have left that out and just shown you the more interesting part; the content handler.
Now most of the interesting parts are commented so you can understand what I'm trying to do.
I have implemented the interface ContentHandler
just to show you that there are more methods available, maybe you'll need one of them in the future. You can however extend from the class DefaultHandler
instead and just overwrite the needed methods. All you do is basically check for the occurrence of certain tags and then trigger certain events upon that. If you want to preserve the order of the elements in the XML file then you simply use a LinkedHashMap
instead of a HashMap
.
I hope it helps.
精彩评论