I am writing the following method to recursively parse XML
data contained a complex SoapObject
(that is a SoapObject
which has SoapObject
as a value of some properties). The SoapObject is the response from a SOAP based web service, which I consumed using ksoap2
In this method, we iterate through all the properties of the passed-in response SoapObject. We check if the property is NOT a SoapObject
(which means it has a simple String
name and a String
value), in which case we simply put the name and value into a HashMap
as a key-value pair. But if the property IS a SoapObject
, we call the method itself (recursive) and pass it this very SoapObject
...
CODE:
static protected Map<String, String> parseComplexSoapObject(SoapObject soapObject) {
Map<String, String> hashMap = new HashMap<String, String>();
//Iterate through the properties starting at top of soapObject
for (int i=0; i<soapObject.getPropertyCount(); i++) {
//Get the current property
Object currentPropertyObject = soapObject.getProperty(i);
//If current property is the last property, check if it is a string or a soap object
if (i==soapObject.getPropertyCount()-1) {
//If it is a string, add the key value pair of property to the map and break the loop. The only statement after the loop will return the map.
if(currentPropertyObject!=null && !(currentPropertyObject instanceof SoapObject)) {
hashMap.put(currentPropertyObject.toString(), currentPropertyObject.toString());
break;
}
//else If it is a soap object, cast the currentPropertyObject to SoapObject and make a recursive call to the function itself, passing the current property so that the method gets "inside" this current property soap object and does all this again
else if(currentPropertyObject!=null && currentPropertyObject.getClass().equals(SoapObject.class)) {
SoapObject currentPropertySoapObject = (SoapObject) currentPropertyObject;
parseComplexSoapObject(currentPropertySoapObject);
}
}
//else if the current property not the last property, check if it is a soap object or a string
else {
//if the current property is a string, add its key value pair to the string and "continue" the loop, so that the loop continues to read the next properties
if(currentPropertyObject!=null && !(currentPropertyObject instanceof SoapObject)) {
hashMap.put(currentPropertyObject.toString(), currentPropertyObject.toString());
continue;
}
//else if the current property is a soap object, cast the currentPropertyObject to SoapObject and make a recursive call to the function itself, passing the current property so that the method gets "inside" this current property soap object and does all this again
else if(currentPropertyObject!=null && currentPropertyObject.getClass().equals(SoapObject.class)) {
SoapObject currentPropertySoapObject = (SoapObject) currentPropertyObject;
parseComplexSoapObject(currentPropertySoapObject);
}
}
}
return hashMap;
}
PROBLEMS:
Now, the problem is that the HashMap
the method returns is created inside this method, so each time the method is recursively called, the HashMap
is recreated and the previous data is lost.
Even if I create the HashMap
outside this method and simple write data to it from the method, the problem will be that there will be no distinction as in which name value pairs have come from which level of nesting (e.g.
<result>
<id>25323205</id>
<name>Cric</name>
<result>
<version>1.0</version>
<result>
<id>445</id>
</result>
will be stored like HashMap = {id=>25323205, name=>Cric, version=>1.0, id=>445}
. I would rather like it this way:
HashMap = {result=>{id=>25323205, name=>Cric}, version=>1.0, result=>{id=>445}}
So what can I do about this?
The point is, that You trying converting multidimensional data into flat structure. It's posible only sometimes under some circumstances. Think if it's really posible for Your data and business needs. I have some ideas what You may do - any may be enought and every useless :)
If multiplication of names is always on different levels of nesting - try to use full name from "body" fe. "result/id", "result/something/id".
You can number similiar tags fe. "id>001", "id>002" or something (needed second map - of ints). Its the way that allows You storing, but slows You down much on reading, searching etc. If Your later just traverse data from beginning to end it will be just enought.
If You need to isolate Your code from ksoap data model, You may try to project SoapObject/Primitive stucture to such built with standard Java objects, but of same complexity and no information lost. This structure for xml will be list of lists.
Here is example class Elem that may hold Name+Value, where value may be Object.That Object will be ArrayList or just String. And the simple method that translates Soap... to new structure:
class Elem{
String name;
Object value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
List<Elem> parseComplexSoapObject(SoapObject soapObject) {
List<Elem> tmp=new ArrayList<Elem>();
PropertyInfo info=new PropertyInfo();
for (int i=0; i<soapObject.getPropertyCount(); i++) {
Object currentPropertyObject = soapObject.getProperty(i);
Elem newElem = new Elem();
if(currentPropertyObject instanceof SoapObject){
SoapObject currentPropertySoapObject = (SoapObject)currentPropertyObject;
info.clear();
soapObject.getPropertyInfo(i, info);
newElem.setName(info.name);
newElem.setValue(parseComplexSoapObject(currentPropertySoapObject));
}else{
//assume primitive
SoapPrimitive currentPropertySoapPrimitive = (SoapPrimitive)currentPropertyObject;
info.clear();
soapObject.getPropertyInfo(i, info);
newElem.setName(info.name);
newElem.setValue(currentPropertySoapPrimitive.getValue().toString());
}
tmp.add(newElem);
}
return tmp;
}