Read xml using XmlPullParser - android

I've the following xml file inside the /mnt/sdcard. I want to get the version of the item type=provider from the following file. The file is big(1500 lines) which has other types also. This is simplified file. In the file I'm interested in this node:
<Item Type="Provider" Version="19.0.0.0"Checksum="EShHVeNtW1xTfEvLvATwqA==" FileSize="2746200" />
From this node I want to get the version i.e. 19.0.0.0.
Here is my xml file:
<Manifest guid="FD29E1EF-A5C4-4D19-ACC8-8C98C7E91B02" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" PackageType="Full" Scope="APPROVED">
<Items>
<Item id="fcxm-8ikj-olk-ffgcxh3" Checksum="EShHVeNtW1xTfEvLvATwqA==" value="f425921f-b6ef-4e58-8a14-fcbd0d7e50e9" />
<Item Type="question" Version="19.0.0.0"Checksum="EShHVeNtW1xTfEvLvATwqA==" FileSize="2746200" />
<Item Type="Provider" Version="19.0.0.0"Checksum="EShHVeNtW1xTfEvLvATwqA==" FileSize="2746200" />
</Items>
</Manifest>
I searched on the internet, I got this which is iterating to all the nodes of item type. I dont want want to iterate.
How can I do this in Android using XmlPullParser?

Hi you can try this,
private void parseContent(XmlPullParser parser)
throws XmlPullParserException,IOException,Exception {
int eventType;
while((eventType=parser.next()) != XmlPullParser.END_TAG) {
if (eventType == XmlPullParser.START_TAG) {
Log.d(MY_DEBUG_TAG,"Parsing Attributes for ["+parser.getName()+"]");
Map<String,String> attributes = getAttributes(parser);
}
else if(eventType==...);
else {
throw new Exception("Invalid tag at content parse");
}
}
}
private Map<String,String> getAttributes(XmlPullParser parser) throws Exception {
Map<String,String> attrs=null;
int acount=parser.getAttributeCount();
if(acount != -1) {
Log.d(MY_DEBUG_TAG,"Attributes for ["+parser.getName()+"]");
attrs = new HashMap<String,String>(acount);
for(int x=0;x<acount;x++) {
Log.d(MY_DEBUG_TAG,"\t["+parser.getAttributeName(x)+"]=" +
"["+parser.getAttributeValue(x)+"]");
attrs.put(parser.getAttributeName(x), parser.getAttributeValue(x));
}
}
else {
throw new Exception("Required entity attributes missing");
}
return attrs;
}

Related

Parsing complex XML using XmlPullParser

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.

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"));
}

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();
}

The #strings don't work in the xml file located the folder res\xml\

I use the following code to get a nameList
I think the content of nameList will be Inbox, Sent, Outbox and Draft, it's my wish.
but in fact, the content of nameList is #string/Inbox, #string/Sent, #string/Outbox and #string/Draft. Why? Thanks!
private void InitVar(){
ArrayList nameList=new ArrayList<String>();
ArrayList valueList=new ArrayList<String>();
ParXML(this, nameList, valueList);
}
public static void ParXML(Context context, List<String> nameList,List<String> valueList) {
XmlResourceParser xrp = context.getResources().getXml(R.xml.msgfolder);
try {
while (xrp.getEventType() != XmlResourceParser.END_DOCUMENT) {
if (xrp.getEventType() == XmlResourceParser.START_TAG) {
String tagName = xrp.getName();
if (tagName.equals("item")) {
nameList.add(xrp.getAttributeValue(null, "name"));
valueList.add(xrp.getAttributeValue(null, "value"));
}
}
xrp.next();
}
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
Values\strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="Inbox">Inbox</string>
<string name="Sent">Sent</string>
<string name="Outbox">Outbox</string>
<string name="Draft">Draft</string>
</resources>
xml\msgfolder.xml
<?xml version="1.0" encoding="utf-8"?>
<MsgHandle>
<item name="#string/Inbox" value="content://sms/inbox"></item>
<item name="#string/Sent" value="content://sms/sent"></item>
<item name="#string/Outbox" value="content://sms/outbox"></item>
<item name="#string/Draft" value="content://sms/draft"></item>
</MsgHandle>
Unlike other resource types, android doesn't resolve resources automatically in res/xml resources. So you have to tell it to do.
First you have to check if the parsed attribute has a resource in it:
int stringResId = xrp.getAttributeResourceValue(null, "name", 0);
if it has stringResId will be non zero. If it is zero then there is no resource identifier in the attribute value.
if (stringResId == 0) {
nameList.add(xrp.getAttributeValue(null, "name")); //<-- Get attribute value as string...
} else {
nameList.add(context.getResources().getString(stringResId)); //<-- Get string resource identified by the stringResId...
}
So the key here is to check with the getAttributeResourceValue which resolves the attribute value as resource is (if it is one).
Hope this helps...

Android Is it possible to define a map in an XML file?

I was trying to define a static hash table that makes use of resources, but I got stonewalled by the impossibility of accessing resources statically.
Then I realized that the best of all places to define a static map is in the resources files themselves.
How can I define a map in XML?
I believe that if possible it should be similar to the Listpreference mechanism, with entries and entries-values.
A simpler option would be to use two arrays. This has the benefit of not iterating the XML file again, uses less code, and is more straightforward to use arrays of different types.
<string-array name="myvariablename_keys">
<item>key1</item>
<item>key1</item>
</string-array>
<string-array name="myvariablename_values">
<item>value1</item>
<item>value2</item>
</string-array>
Then your java code would look like this:
String[] keys = this.getResources().getStringArray(R.array.myvariablename_keys);
String[] values = this.getResources().getStringArray(R.array.myvariablename_values);
LinkedHashMap<String,String> map = new LinkedHashMap<String,String>();
for (int i = 0; i < Math.min(keys.length, values.length); ++i) {
map.put(keys[i], values[i]);
}
How can I define a map in XML?
<thisIsMyMap>
<entry key="foo">bar</entry>
<entry key="goo">baz</entry>
<!-- as many more as your heart desires -->
</thisIsMyMap>
Put this in res/xml/, and load it using getResources().getXml(). Walk the events to build up a HashMap<String, String>.
You can always embed Json inside your strings.xml file:
res/values/strings.xml
<string name="my_map">{"F":"FOO","B":"BAR"}</string>
And inside your Activity, you can build your Map in the onStart method:
private HashMap<String, String> myMap;
#Override
protected void onStart() {
super.onStart();
myMap = new Gson().fromJson(getString(R.string.my_map), new TypeToken<HashMap<String, String>>(){}.getType());
}
This code needs Google Gson API to work. You can do it using the built-in Json API in the Android SDK.
And As for accessing the Map statically, you can create a static method:
private static HashMap<String, String> method(Context context) {
HashMap<String, String> myMap = new Gson().fromJson(context.getString(R.string.serve_times), new TypeToken<HashMap<String, String>>(){}.getType());
return myMap;
}
The correct answer was mentioned by CommonsWare above, but as XML-parsing is not so simple, as following a simple parser for this purpose:
public static Map<String, String> getHashMapResource(Context context, int hashMapResId) {
Map<String, String> map = new HashMap<>();
XmlResourceParser parser = context.getResources().getXml(hashMapResId);
String key = null, value = null;
try {
int eventType = parser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
if (parser.getName().equals("entry")) {
key = parser.getAttributeValue(null, "key");
if (null == key) {
parser.close();
return null;
}
}
}
else if (eventType == XmlPullParser.END_TAG) {
if (parser.getName().equals("entry")) {
map.put(key, value);
key = null;
value = null;
}
} else if (eventType == XmlPullParser.TEXT) {
if (null != key) {
value = parser.getText();
}
}
eventType = parser.next();
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
return map;
}
Android often works with DefaultsXmlParser.getDefaultsFromXml() which parse next syntax:
<?xml version="1.0" encoding="utf-8"?>
<defaults>
<entry>
<key>api_url</key>
<value>https://</value>
</entry>
<entry>
<key>some_feature_flag</key>
<value>true</value>
</entry>
</defaults>
And read map:
val map = DefaultsXmlParser.getDefaultsFromXml(this, R.xml.my_map)

Categories

Resources