0

In the below code --> mapcolRows.put(subchildelement.getName(),subchildelement.getText()); subchildelement.getName() gets 'Enabled' and 'Name' tags in XML as shown below

<Profile xmlns="http://soap.sforce.com/2006/04/metadata">
<custom>true</custom>
<userLicense>Salesforce</userLicense>
<userPermissions>
    <enabled>true</enabled>
    <name>AllowViewKnowledge</name>
</userPermissions>
<userPermissions>
    <enabled>true</enabled>
    <name>ApiEnabled</name>
</userPermissions>
<userPermissions>
    <enabled>true</enabled>
    <name>ChatterForSharePoint</name>
</userPermissions>
<userPermissions>
    <enabled>true</enabled>
    <name>ChatterInternalUser</name>
</userPermissions>
<userPermissions>
    <enabled>true</enabled>
    <name>ChatterInviteExternalUsers</name>
</userPermissions>
<userPermissions>
    <enabled>true</enabled>
    <name>ChatterOwnGroups</name>
</userPermissions>
<userPermissions>
    <enabled>true</enabled>
    <name>ConvertLeads</name>
</userPermissions>
<userPermissions>
    <enabled>true</enabled>
    <name>CreateCustomizeFilters</name>
</userPermissions>
<userPermissions>
    <enabled>true</enabled>
    <name>CreateCustomizeReports</name>
</userPermissions>
<userPermissions>
    <enabled>true</enabled>
    <name>CreateTopics</name>
</userPermissions>
<userPermissions>
    <enabled>true</enabled>
    <name>DistributeFromPersWksp</name>
</userPermissions>
<userPermissions>
    <enabled>true</enabled>
    <name>EditEvent</name>
</userPermissions>
<userPermissions>
    <enabled>true</enabled>
    <name>EditOppLineItemUnitPrice</name>
</userPermissions>
<userPermissions>
    <enabled>true</enabled>
    <name>EditTask</name>
</userPermissions>
<userPermissions>
    <enabled>true</enabled>
    <name>EditTopics</name>
</userPermissions>

Image shows the out of XML

I would like to achieve 'name' tag first and 'enabled' tag below 'name' tag as shown

<userPermissions>
 <name>AllowViewKnowledge</name>    
 <enabled>true</enabled>

 for(Dom.XmlNode subchildelement:childlist[0].getChildElements())
 {
      System.debug('------------------1subchildelement : ' + subchildelement);
      subChildElementName = subchildelement.getName();
      System.debug('------------------2subChildElementName : ' + subChildElementName);
      subChildElementsList.add(subChildElementName);
      elementValues = subchildelement.getText();
      System.debug('------------------3elementValues ' + elementValues); 
      elementValuesList.add(elementValues);
 }

 for(Dom.XmlNode childelement : rootElement.getChildElements())    
 {    
      System.debug('------------------11childelement : ' + childelement);
      mapcolRows = new map<String,String>();
      System.debug('------------------22mapcolRows : ' + mapcolRows);
      for (Dom.XmlNode subchildelement:childelement.getChildElements())
      {
           System.debug('------------------33subchildelement getName: ' + subchildelement.getName());
           System.debug('------------------333subchildelement getText: ' + subchildelement.getText());
           mapcolRows.put(subchildelement.getName(),subchildelement.getText());
           //mapcolRows.put(subchildelement.getText(),subchildelement.getName());
           System.debug('------------------44mapcolRows : ' + mapcolRows);
      }

      rowValues.add(mapcolRows);  //Add rowValuds list to here
      System.debug('------------------55rowValues : ' + rowValues);
      ApexPages.addmessage(new ApexPages.message(ApexPages.severity.INFO,'Response is :' + rowValues));

 }

Once the order of XML tags is changed the output of the Table printing in Visualforce page should be as shown below

enter image description here

  • I'd suggest you look at one of the 3rd party xml editing tools to parse the file with the output in the format you'd like. Take a look at XML Validator Buddy. It may do what you're looking for since it can do transformations. – crmprogdev Feb 28 '17 at 14:21
2

Trying to transform your xml, and then parsing it, is definitely the hard way of accomplishing this.

My preferred method of handling situations like this is to create an Apex class (with inner classes as appropriate) to recursively parse some given xml and, crucially, store the resulting data.

The idea is that if you have your data stored in a class instance, it doesn't matter what order the data is in because you can fetch data from a class in any order that you want.

The XML that you're working with is pretty simple. A partial example of a parsing/storage class for that XML is

public class Permissions{
    // 'Permission' is an inner class, which is defined below.
    // I find it helpful to have class names and variable names mirror the names used
    //   in the XML document as closely as possible.
    public List<Permission> userPermissions;

    public Permissions(String xmlInput){
        Dom.Document inputDoc = new Dom.Document();
        inputDoc.load(xmlInput);

        process(inputDoc.getRootElement());
    }

    // If you're going to use this in a visualforce page, you'll need to manually
    //   implement getter methods.
    public List<Permission> getUserPermissions(){
        return userPermissions;
    }

    // Each inner class will need a 'process' method like this one.
    private void process(Dom.XmlNode inNode){
        String currentNodeName;
        userPermissions = new List<Permission>();

        for(Dom.XmlNode childNode :inNode.getChildElements()){
            currentNodeName = childNode.getName();

            // When we come across a node that we want to handle, we simply create
            //   a new instance of the appropriate inner class, and pass the node
            //   to its process() method.
            // Each inner class should specialize in handling a specific type of XmlNode.
            // This helps to prevent the parsing logic from getting too unwieldy in 
            //   any one class.
            if(currentNodeName == 'userPermissions'){
                Permissions perm = new Permission();
                perm.process(inNode);

                // After we're done processing the current node, we can add it
                //   to the list.
                userPermissions.add(perm);
            }
        }
    }

    // When we get down to a level where a child element contains just a string,
    //   then there's little use in creating one more level of inner classes.
    // Such child elements can just be made variables of this class.
    public class Permission{
        // Again, variable names mirror those found in the XML document.
        public String name;
        public Boolean enabled;

        public Permission(){
        }

        // Again, getters are required for visualforce
        public Boolean getEnabled(){
            return enabled;
        }

        public String getName(){
            return name;
        }

        // This method should look like it has something in common with the 'process()'
        //   method of the outer class.
        // Having the same general structure makes this pattern easy to expand to
        //   be able to parse more complicated XML structures.
        public void process(Dom.XmlNode inNode){
            String currentNodeName;

            for(Dom.XmlNode childNode :inNode.getChildElements()){
                currentNodeName = childNode.getName();

                // Note: The order that you check currentNodeName values in doesn't matter at all
                if(currentNodeName == 'enabled'){
                    // For everything that isn't a string, we'll need to use the
                    //   appropriate 'valueOf()' method to convert to the correct type
                    enabled = Boolean.valueOf(childNode.getText());
                } else if (currentNodeName == 'name'){
                    // For strings, we don't need any special treatment
                    name = childNode.getText();
                }
            }
        }
    }
}

Yes, it's a bit more typing (~58 lines without the comments, compared to the 30 lines for your provided code), and it does get tedious for more complicated XML structures, but I believe that this approach makes up for that in flexibility, maintainability, readability, and extensibility.

| improve this answer | |
  • Very nicely explained. – Saroj Bera Mar 1 '17 at 10:10
2

Better you can create an inner wrapper class UserPermissions with member variables enabled and name. Parse the XML create a list of UserPermissions. Then loop through the UserPermissions list and do whatever you want.

This is the loop where you will get the list of UserPermissions.

for (UserPermissions usp:lstUserPerms){
    System.debug(usp.name);
    System.debug(usp.enabled);
}

Complete Parsing code

public class XMLParsing {
    public XMLParsing(){
        String xmlString = '<Profile xmlns="http://soap.sforce.com/2006/04/metadata">  '  + 
 '       <custom>true</custom>  '  + 
 '       <userLicense>Salesforce</userLicense>  '  + 
 '       <userPermissions>  '  + 
 '           <enabled>true</enabled>  '  + 
 '           <name>AllowViewKnowledge</name>  '  + 
 '       </userPermissions>  '  + 
 '       <userPermissions>  '  + 
 '           <enabled>true</enabled>  '  + 
 '           <name>ApiEnabled</name>  '  + 
 '       </userPermissions>  '  + 
 '       <userPermissions>  '  + 
 '           <enabled>true</enabled>  '  + 
 '           <name>ChatterForSharePoint</name>  '  + 
 '       </userPermissions>  '  + 
 '       <userPermissions>  '  + 
 '           <enabled>true</enabled>  '  + 
 '           <name>ChatterInternalUser</name>  '  + 
 '       </userPermissions>  '  + 
 '       <userPermissions>  '  + 
 '           <enabled>true</enabled>  '  + 
 '           <name>ChatterInviteExternalUsers</name>  '  + 
 '       </userPermissions>  '  + 
 '       <userPermissions>  '  + 
 '           <enabled>true</enabled>  '  + 
 '           <name>ChatterOwnGroups</name>  '  + 
 '       </userPermissions>  '  + 
 '       <userPermissions>  '  + 
 '           <enabled>true</enabled>  '  + 
 '           <name>ConvertLeads</name>  '  + 
 '       </userPermissions>  '  + 
 '       <userPermissions>  '  + 
 '           <enabled>true</enabled>  '  + 
 '           <name>CreateCustomizeFilters</name>  '  + 
 '       </userPermissions>  '  + 
 '       <userPermissions>  '  + 
 '           <enabled>true</enabled>  '  + 
 '           <name>CreateCustomizeReports</name>  '  + 
 '       </userPermissions>  '  + 
 '       <userPermissions>  '  + 
 '           <enabled>true</enabled>  '  + 
 '           <name>CreateTopics</name>  '  + 
 '       </userPermissions>  '  + 
 '       <userPermissions>  '  + 
 '           <enabled>true</enabled>  '  + 
 '           <name>DistributeFromPersWksp</name>  '  + 
 '       </userPermissions>  '  + 
 '       <userPermissions>  '  + 
 '           <enabled>true</enabled>  '  + 
 '           <name>EditEvent</name>  '  + 
 '       </userPermissions>  '  + 
 '       <userPermissions>  '  + 
 '           <enabled>true</enabled>  '  + 
 '           <name>EditOppLineItemUnitPrice</name>  '  + 
 '       </userPermissions>  '  + 
 '       <userPermissions>  '  + 
 '           <enabled>true</enabled>  '  + 
 '           <name>EditTask</name>  '  + 
 '       </userPermissions>  '  + 
 '       <userPermissions>  '  + 
 '           <enabled>true</enabled>  '  + 
 '           <name>EditTopics</name>  '  + 
 '       </userPermissions>  '  + 
 '  </Profile>  ' ; 
        Dom.Document docx = new Dom.Document();
        docx.load(xmlString);
        Dom.XmlNode xroot = docx.getRootElement();
        Dom.XmlNode[] childList = xroot.getChildElements();
        Dom.XmlNode[] ichildList;
        UserPermissions up;
        List<UserPermissions> lstUserPerms = new List<UserPermissions>();
        for(Dom.XmlNode child:childList){
            if(child.getName().equals('userPermissions')){
                ichildList = child.getChildElements();
                up = new UserPermissions();
                for(Dom.XmlNode ichild:ichildList){
                    if(ichild.getName().equals('enabled')){
                        up.enabled = ichild.getText();
                    }
                    if(ichild.getName().equals('name')){
                        up.name = ichild.getText();
                    }                 
                }
                lstUserPerms.add(up);
            }
        }
        for (UserPermissions usp:lstUserPerms){
            System.debug(usp.name);
            System.debug(usp.enabled);
        }
    }
    class UserPermissions{
        String enabled {get;set;}
        String name {get;set;}       
    }
}
| improve this answer | |
0

You can use the XML Serialization utility I wrote : https://github.com/skolakan/Apex-XML-Serializer

Here is the wrapper class that you can use

public class SampleController {    

    public ProfileModel Profile {get;set;}

    public string toXML(){
        XMLSerializer serializer = new XMLSerializer();
        return serializer.serialize(this);
    }    

    public class ProfileModel{
        public UserPermissionsModel[] userPermissions{get; set;}
    }

    public class UserPermissionsModel{
        public string name{get; set;}
        public string enabled {get; set;}
    }
}

Then use it like

string xmlString = '<Your XML String Here>';
List<String> deSerializeAsArrayNodes = new List<String>{'userPermissions'};
SampleController controller = (SampleController)XMLSerializer.deSerialize(xmlString, SampleController.Class,deSerializeAsArrayNodes);
System.debug('output:' + controller.toXML());

Xml gets deserialized into Apex object irrespective of the order and when you serialize it again to XML, it maintains order as defined in the class definition. Hope that helps!

| improve this answer | |
  • So if you swap name and enabled, then the latter property would instead be listed first? – Adrian Larson Feb 28 '17 at 22:38
  • @AdrianLarson, That is correct. – codeflare Mar 1 '17 at 13:33
  • Hi @codeflare , When I implemented above I am getting below error Method does not exist or incorrect signature: XMLSerializer.deSerialize(String, Type, List<String>) at line 155 column 91 PLease Help!!! – Rajesh Mar 7 '17 at 13:13
  • Please post your code. – codeflare Mar 7 '17 at 15:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.