Parsing complex XML using XmlPullParser - android

I'm facing the problem of parsing xml using XmlPullParser. Everithing works fine except this problmatic part:
<Device>
<Description>
Tracker, type CONNECT
<Firmware>0240</Firmware>
</Description>
<Settings>
...
</Settings>
<Variables>
...
</Variables>
</Device>
I need to parse both DESCRIPTION and FIRMWARE. But I can't read properly that description text because of such tags weird structure.
What I've tried (following this guide):
private Device parseDevice(XmlPullParser parser) throws XmlPullParserException, IOException {
Device device = new Device();
parser.require(XmlPullParser.START_TAG, ns, DEVICE);
//device.setDescription(readDeviceDescription(parser)); <---tried to parse from here
device.setName(readDeviceName(parser));
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) {
continue;
}
String name = parser.getName();
// Starts by looking for the entry tag
switch (name) {
case DESCRIPTION:
// device.setDescription(readDeviceDescription(parser)); <---and from here
device.setFirmware(readDescription(parser, device)); //<-- and inside this method
break;
case VARIABLES:
device.setGroups(readGroups(parser));
break;
default:
skip(parser);
break;
}
}
return device;
}
readDeviceDesscription() method (maybe problem lies here):
private String readDeviceDescription(XmlPullParser parser) throws XmlPullParserException, IOException {
String result = "";
if (parser.next() == XmlPullParser.TEXT) {
result = parser.getText();
parser.next();
}
return result;
}
But any my attempt was ending with returning null either to Firmware or to Description.
Please help. Appreciate any hint.

You should do:
private String readDeviceDescription(XmlPullParser parser) throws XmlPullParserException, IOException {
String result = parser.getText();
return result;
}
Since you are already positioned at Description start_tag getText call will return the text inside Description tag.
To get the Firmware tag text you should do:
if(parser.getEventType() == XmlPullParser.START_TAG && parser.getName().compareTo("Firmware")==0)
String firmwareText = parser.getText();
Also take a look at this its a good example of a clean XmlPullParser implementation.
Hope this helps.

Related

XmlPullParser handling multidimensional repeated elements

Im trying to get a list of the top level elements from my XML (that contains duplicated sub elements)
example XML
<feed>
<folder name="subfolder1">
<file name="subfile1" />
<file name="subfile2" />
<folder name="subsubfolder1">
<file name="subsubfile1" />
<file name="subsubfile2" />
</folder>
</folder>
<folder name="subfolder2">
<file name="subfile1" />
<file name="subfile2" />
<folder name="subsubfolder1">
<file name="subsubfile1" />
<file name="subsubfile2" />
</folder>
</folder>
<file name="file1"/>
</feed>
I'm trying to get a list of all names of the top level elements e.g.
.subfolder1
.subfolder2
Here is my FeedReader....
private List<Entry> readFeed(XmlPullParser parser) throws XmlPullParserException, IOException {
List<Entry> entries = new ArrayList<Entry>();
Log.v("ab", "reed feed started");
parser.require(XmlPullParser.START_TAG, ns, "feed");
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) {
continue;
}
String sectionName = parser.getName();
if(sectionName.equals("folder")) {
readFolder(parser);
}
}
return entries;
}
private void readFolder (XmlPullParser parser) throws XmlPullParserException, IOException {
parser.require(XmlPullParser.START_TAG, ns, "folder");
Log.v("ab", parser.getAttributeValue(null, "name"));
parser.require(XmlPullParser.END_TAG, ns, "folder");
}
And here is my LogCat....
09-02 13:40:22.537 31736-31753/? V/ab reed feed started
09-02 13:40:22.537 31736-31753/? V/abīš• subfolder1
Can anyone help with why this is stopping after finding the first instance of an folder element?
It looks like a problem with your last parser.require line:
parser.require(XmlPullParser.END_TAG, ns, "folder");
From the documentation, what you're doing here is checking if these conditions are met, and if not, throwing an exception. So you're currently at the 'folder' start tag that you've just read, and you're checking if you're at the 'folder' end tag. Since you're not at the 'folder' end tag, then parser.require will throw an exception.
If you remove that line it should just let your while loop keep going until the next folder start tag.
Edit: here's a full solution
We need to keep going until the end of the document not just until any END_TAG so I amended your while loop to while (parser.next() != XmlPullParser.END_DOCUMENT), then added some extra code after the readFolder method call. If I understood correctly, you are only after the folders named 'subfolder' and skipping the 'subsubfolder's. So I've included a loop which should skip those.
I also removed the parser.require lines as I didn't see the need personally, but this is just one way to do it.
private List<Entry> readFeed(XmlPullParser parser) throws XmlPullParserException, IOException {
List<Entry> entries = new ArrayList<Entry>();
Log.v("ab", "reed feed started");
while (parser.next() != XmlPullParser.END_DOCUMENT) {
if (parser.getEventType() != XmlPullParser.START_TAG) {
continue;
}
String sectionName = parser.getName();
if(sectionName.equals("folder")) {
readFolder(parser);
//these booleans will be used to help us skip the subfolders
boolean finishedTopLevelElement = false;
boolean unwantedSubFolderFound = false;
//this will loop until we are at a "folder" tag and have
//confirmed we have finished with the top level folder
while (!(("folder".equals(parser.getName())) && finishedTopLevelElement)){
parser.next();
//we only care about 'folder' tags, for anything else
//we keep looping
if ("folder".equals(parser.getName())){
if (parser.getEventType() == XmlPullParser.START_TAG){
//if we hit a folder start tag, we're at a sub-folder
unwantedSubFolderFound = true;
} else if (parser.getEventType() == XmlPullParser.END_TAG && !unwantedSubFolderFound){
//if we hit a 'folder' end tag and we've not got an unwanted subfolder then
//we're done, it's the end tag of the top-level folder
finishedTopLevelElement = true;
unwantedSubFolderFound = false;
} else {
//if it's a folder end tag and we HAVE previously found an unwanted sub folder start tag
//then we've successfully skipped that sub-folder and can keep looking
unwantedSubFolderFound = false;
}
}
}
}
}
return entries;
}
private void readFolder (XmlPullParser parser) throws XmlPullParserException, IOException {
Log.v("ab", parser.getAttributeValue(null, "name"));
}

android parsing xml content from String

Following is the xml content which i get after decrypting an encrypted value
<card order_no="1" id="cmpe0rhm3ym5ha8wlqp4jt7u" place="HOME">33</card>
Now i have the above content in a String called message.
Now i want parse the values such as id and place using the main tag "card" and the number
"33" also to be parsed.
Following is what i have been tried
InputStream inputStream = new ByteArrayInputStream(message.getBytes());
XmlPullParser cardParser = Xml.newPullParser();
cardParser.setInput(inputStream, null);
Map<String, String> attrs = XMLParsers.getAttributes(cardParser);
String cardTag = cardParser.getName();
if (cardTag.equalsIgnoreCase("card"))
{
CardTag card = new CardTag();
card.setId(attrs.get("id"));
card.setPlace(attrs.get("place"));
card.setCardNumericValue(cardParser.getText());
return card;
}
I have stored the string in an InputStream and again tried to parse it, but i am getting a null pointer exception
Value of String cardTag seems to be null when i printed it.
NullPointerException rises at if condition "if (cardTag.equalsIgnoreCase("card"))"
How to do this
The documentation says
For START_TAG or END_TAG events, the (local) name of the current
element is returned when namespaces are enabled. When namespace
processing is disabled, the raw name is returned. For ENTITY_REF
events, the entity name is returned. If the current event is not
START_TAG, END_TAG, or ENTITY_REF, null is returned.
So, I believe you current event is START_DOCUMENT and you need call cardParser.next() to get next start tag event. Please take a look on example
public class SimpleXmlPullApp{
public static void main (String args[])
throws XmlPullParserException, IOException
{
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser xpp = factory.newPullParser();
xpp.setInput( new StringReader ( "<foo>Hello World!</foo>" ) );
int eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if(eventType == XmlPullParser.START_DOCUMENT) {
System.out.println("Start document");
} else if(eventType == XmlPullParser.START_TAG) {
System.out.println("Start tag "+xpp.getName());
} else if(eventType == XmlPullParser.END_TAG) {
System.out.println("End tag "+xpp.getName());
} else if(eventType == XmlPullParser.TEXT) {
System.out.println("Text "+xpp.getText());
}
eventType = xpp.next();
}
System.out.println("End document");
}
}
output
Start document
Start tag foo
Text Hello World!
End tag foo
End document

Parse 2 Levels of XML android

Please correct the table if it is wrong, ,I'm not entirely sure what to title this.
I am trying to parse my XML in android and i have code that should work, but the problem is there are multiple levels of tags i need to get into.
Here is an example of my XML:
<dsxout>
<uselessTag>unnecasary info</uselessTag>
<results>
<listing>
<title>I'm a Title</title>
<description>very amusing description</description>
</listing>
<listing>
...
</listing>
</results>
</dsxout>
Here is where my parsing gets stuck:
private List readFeed(XmlPullParser parser) throws XmlPullParserException, IOException {
List entries = new ArrayList();
Toast.makeText(getBaseContext(), "readFeed", Toast.LENGTH_SHORT).show();
parser.require(XmlPullParser.START_TAG, ns, "dsxout");
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) {
continue;
}
String name = parser.getName();
The line below just spits out the tag uselessTag and results
Toast.makeText(getBaseContext(), name, Toast.LENGTH_SHORT).show();
// Starts by looking for the entry tag
if (name.equals("listing")) {
entries.add(readEntry(parser));
} else {
skip(parser);
}
}
return entries;
}
The problem is it only reads to the second level of tags and not inside the results tag so it cannot find listing.
How do I go another level in while reading?
Adding this statement seems to have fixed the problem
if (name.equals("results")) {
String nextTokedn = "Next Level " + parser.nextTag();
}

XMLPullParser checking for specific Child node

I have a XML like this:
<node_a>
<node_b>
<required/>
<random_node1/>
</node_b>
<node_c>
<required/>
</node_c>
<node_d>
<random_node2/>
</node_d>
</node_a>
and trying to parse it using XMLPullParser
I want to iterate through the XML and add all the node names that have the child . In this example i my result list should have node_b and node_c.
The problem i face is if i do a parser.next() then the pointer moves ahead and it is impossible for me get back and iterate through them again. There is no api to check for all child nodes.
What will be the best approach to go with.
Something like this?
XmlPullParser paser = Xml.newPullParser();
... other init that you might need ...
parser.next(); // get first token
// In general, you'll need to add error checking such as this:
if (parser.getEventType() != XmlPullParser.START_TAG)
...error...
String parentName = parser.getName(); // this will be "node_a"
parser.next(); // done with first token; fetch next
while (parser.getEventType() == XmlPullParser.START_TAG)
{
String childName = parser.getName(); // will be "node_b" first time through loop
// get nested attributes - e.g. "required"
parser.next();
while (parser.getEventType() == XmlPullParser.START_TAG)
{
String nestedAttribute = parser.getName();
... do something with nestedAttribute ...
parser.next();
}
if (parser.getEventType() != XmlPullParser.END_TAG)
...error...
parser.next(); // consume END_TAG for nested attributes
}
if (parser.getEventType() != XmlPullParser.END_TAG)
...error...
// make sure we're at end of file
parser.next(); // consume END_TAG for node_a
if (parser.getEventType() != XmlPullPaarser.END_DOCUMENT)
...error...

Android, XMLParser - Reading XML

im currently trying to read and XML from a web server in my Android App, but im not sure how to search for the TAGS, and all the examples i see are not unlike mine:
<document>
<producer/>
<metadata></metadata>
<recorddata count="111">
<row>
<field name="numint" value="MTAwMQ=="/>
<field name="Grupo" value="NQ=="/>
<field name="Link" value="Q29ycmllbnRlcw=="/>
<field name="Nombre" value="Q29ycmllbnRlcw=="/>
<field name="Valor" value="MzQwMC4wMA=="/>
</row>
</recorddata>
</document>
I need to read the ROWS inside RECORDATA, but im not sure how to address this problem, this is my code:
private void parseXML(XmlPullParser parser) throws XmlPullParserException,IOException
{
ArrayList<Record> Records = null;
int eventType = parser.getEventType();
Record currentRecord = null;
while (eventType != XmlPullParser.END_DOCUMENT){
String name = null;
switch (eventType){
case XmlPullParser.START_DOCUMENT:
Records = new ArrayList();
break;
case XmlPullParser.START_TAG:
name = parser.getName();
if (name == "row"){
currentRecord = new Record();
} else if (currentRecord != null){
if (name == "numint"){
currentRecord.numint = parser.getText();
} else if (name == "Grupo"){
currentRecord.group = parser.getText();
} else if (name == "Link"){
currentRecord.link= parser.getText();
}else if (name == "Nombre") {
currentRecord.name= parser.getText();
}else if (name == "Valor") {
currentRecord.value= parser.getText();
}
}
break;
case XmlPullParser.END_TAG:
name = parser.getName();
if (name.equalsIgnoreCase("row") && currentRecord != null){
Records.add(currentRecord);
}
}
eventType = parser.next();
}
printProducts(Records);
}
BUT, the Records List IS EMPTY at the end of the process, and i can tell is doing something, because there are a lot of records and it takes like 2 minutes till the process is over.
Im not sure if im using the getName() correctly, should i be checking for field??? and if so, how do i know in which field im on. Should i be using nextToken() instead???
I Solved the problem by rewriting my code as follows:
case XmlPullParser.START_TAG:
name = parser.getName();
if (name.equals("row")){
currentRecord = new Record();
} else if (currentRecord != null) {
if(parser.getName().equals("field")) {
if(parser.getAttributeValue(null, "name").equalsIgnoreCase("numint")) {
currentRecord.numint = parser.getAttributeValue(null, "value");
}else if (parser.getAttributeValue(null, "name").equalsIgnoreCase("Grupo")) {
currentRecord.group = parser.getAttributeValue(null, "value");
}else if (parser.getAttributeValue(null, "name").equalsIgnoreCase("Link")) {
currentRecord.link = parser.getAttributeValue(null, "value");
}else if (parser.getAttributeValue(null, "name").equalsIgnoreCase("Nombre")) {
currentRecord.name = parser.getAttributeValue(null, "value");
}else if (parser.getAttributeValue(null, "name").equalsIgnoreCase("Valor")) {
currentRecord.value = parser.getAttributeValue(null, "value");
}
}
}
break;
Taking something the answer by Andreaoid, and a little tweaking, i could the read de xml file without issue
To get an attribute you can use the method:
parser.getAttributeValue(null, "value");
Definition:
public abstract String getAttributeValue (String namespace, String name)
Taken from help:
*Added in API level 1. Returns the attributes value identified by namespace URI and namespace localName. If namespaces are disabled namespace must be null. If current event type is not START_TAG then IndexOutOfBoundsException will be thrown.*
Pay attention: Replace all the String comparisons!
name == "row"
With:
name.equals("row")
Hope it helps.

Categories

Resources