Your parsing code has a number of issues related to parsing the XML.
First, the inner members are called "members", not "member", so this line needs to be fixed:
if(ch.getName()=='members')
Next, if ch.getName() is equal to 'members', it won't be equal to 'name', because that'll be in a later pass; you can't nest your if statements like that.
Your next problem is then going to be here:
memstr.put(string.valueOf(ch.getParent()),ch.getText());
because it's just building really big, useless keys for you.
Also, you never actually assigned the value back to jsonString, so the results are getting lost.
I get where you're trying to go with this, but XML parsing is non-trivial to get right.
Also, "members" may be an array once converted, which is why it's pluralized.
I'm not sure exactly how to fix your code, but I happen to have some public code that I'll share with you: XmlToJSON.
Since we can't readily determine if we'll need the array syntax, and because my class automatically generates arrays, we can start with the automatic untyped JSON parsing:
public class Doccox{
public Blob fil { get; set; }
public string jsonString { get; set; }
public void parse() {
try {
Dom.Document doc = new Dom.Document();
doc.load(fil.toString());
jsonString = XmlToJson.parseDocumentToJson(doc);
} catch(Exception e) {
}
}
}
There's some additional processing you need to do if you want something other than a JSON string. To get it into a wrapper, you have to be aware that types
will be either an Object or an Array, depending on if it has one or more elements. Similarly, members will either be an Object or an Array.
As a minor optimization in this case, we can alter XmlToJson to assume that elements ending with S will always be an array:
global class XmlToJson {
public String xmlText { get; set; }
public String getJsonText() {
try {
Dom.Document doc = new Dom.Document();
doc.load(xmlText);
return parseDocumentToJson(doc);
} catch(Exception e) {
return '';
}
}
// Try to determine some data types by pattern
static Pattern
boolPat = Pattern.compile('^(true|false)$'), decPat = Pattern.compile('^[-+]?\\d+(\\.\\d+)?$'),
datePat = Pattern.compile('^\\d{4}.\\d{2}.\\d{2}$'),
timePat = Pattern.compile('^\\d{4}.\\d{2}.\\d{2} (\\d{2}:\\d{2}:\\d{2} ([-+]\\d{2}:\\d{2})?)?$');
// Primary function to decode XML
static Map<Object, Object> parseNode(Dom.XmlNode node, Map<Object, Object> parent) {
// Iterate over all child elements for a given node
for(Dom.XmlNode child: node.getChildElements()) {
// Pull out some information
String nodeText = child.getText().trim(), name = child.getName();
// Determine data type
Object value =
// Nothing
String.isBlank(nodeText)? null:
// Try boolean
boolPat.matcher(nodeText).find()?
(Object)Boolean.valueOf(nodeText):
// Try decimals
decPat.matcher(nodeText).find()?
(Object)Decimal.valueOf(nodeText):
// Try dates
datePat.matcher(nodeText).find()?
(Object)Date.valueOf(nodeText):
// Try times
timePat.matcher(nodeText).find()?
(Object)DateTime.valueOf(nodeText):
// Give up, use plain text
(Object)nodeText;
// We have some text to process
if(value != null) {
// This is a pluralized word, make list
if(name.endsWith('s')) {
// Add a new list if none exists
if(!parent.containsKey(name)) {
parent.put(name, new List<Object>());
}
// Add the value to the list
((List<Object>)parent.get(name)).add(value);
} else {
// Store a new value
parent.put(name, value);
}
} else if(child.getNodeType() == Dom.XmlNodeType.ELEMENT) {
// If it's not a comment or text, we will recursively process the data
Map<Object, Object> temp = parseNode(child, new Map<Object, Object>());
// If at least one node was processed, add a new element into the array
if(!temp.isEmpty()) {
// Again, create or update a list
if(parent.containsKey(name)) {
try {
// If it's already a list, add it
((List<Object>)parent.get(name)).add(temp);
} catch(Exception e) {
// Otherwise, convert the element into a list
parent.put(name, new List<Object> { parent.get(name), temp });
}
} else {
// New element
parent.put(name, temp);
}
}
}
}
return parent;
}
// This function converts XML into a Map
global static Map<Object, Object> parseDocumentToMap(Dom.Document doc) {
return parseNode(doc.getRootElement(), new Map<Object, Object>());
}
// This function converts XML into a JSON string
global static String parseDocumentToJson(Dom.Document doc) {
return JSON.serializePretty(parseDocumentToMap(doc));
}
// This function converts XML into a native object
// If arrays are expected, but not converted automatically, this call may fail
// If so, use the parseDocumentToMap function instead and fix any problems
global static Object parseDocumentToObject(Dom.Document doc, Type klass) {
return JSON.deserialize(parseDocumentToJson(doc), klass);
}
}
The only change here is to swap out the automatic list aggregation with automatic list assumption. This works out well for the package.xml, because the naming convention happens to work out that way.
Here's an XML file I use by default:
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
<types>
<members>*</members>
<name>ApexClass</name>
</types>
<types>
<members>*</members>
<name>ApexComponent</name>
</types>
<types>
<members>*</members>
<name>ApexPage</name>
</types>
<types>
<members>*</members>
<name>ApexTrigger</name>
</types>
<types>
<members>*</members>
<name>StaticResource</name>
</types>
<version>37.0</version>
</Package>
And here's the resulting output:
{ "version" : 37.0,
"types" : [
{ "name" : "ApexClass", "members" : [ "*" ] },
{ "name" : "ApexComponent", "members" : [ "*" ] },
{ "name" : "ApexPage", "members" : [ "*" ] },
{ "name" : "ApexTrigger", "members" : [ "*" ] },
{ "name" : "StaticResource", "members" : [ "*" ] }
]
}
There's also a corresponding unit test in the original link, above, but you'll have to modify it in order to maintain 100% coverage.
Best Answer
The URL is on the "next level down", so your code would look something more like this: