[SalesForce] DOM Document with XMLNode class does not giving perfect result

I need to implement a visual force page which contains two inputTextArea boxes. These textboxes should form like one box is left side for input and another one is right side to display result. This page also should contains a button.

The input and output will be like below.

Input:

Input text box will take Xml document as input

Output:

output need to be display like XML root elements, child elements names, child elements vales and attributes names and their vales if they have attributes by clicking on button.

To get this requirement, i have implemented the following class and Visual force page. These are working in successive way except the one case. The only case is, if i give a xml, the output is coming like below which is shown in image.

original result

I need to remove the empty space between Element Name:book, Element Value:, Attribute Name: id which means the result should be come like below.

My expectation result

XML format should follow a format like shown images. It should not follow any other way.

I have tried a lot to remove the space between elements. But, i couldn't get it. Can anyone please help me to get out of this problem.

Please find the VF Page and class:

VF Page:

<apex:page controller="ParseTest3">
    <apex:form >
        <apex:pageBlock id="refresh">
          <apex:pageMessages />
            <apex:inputTextarea style="width:300px;height:300px;" value="{!parsingXmlFile}"/>&nbsp; &nbsp;
            <apex:inputTextarea style="width:300px;height:300px;" value="{!resultFile}" />
            <apex:pageBlockButtons location="bottom">
                <apex:commandButton value="Parse the file" reRender="refresh" action="{!parseFile}"/>
            </apex:pageBlockButtons>
        </apex:pageBlock>
    </apex:form>
</apex:page>

Class:

public class ParseTest3 {

    public String parsingXmlFile{set;get;}
    public String resultFile{set;get;}

    public void parseFile(){

            Dom.Document doc = new Dom.Document();
            doc.load(parsingXmlFile);
            Dom.XmlNode node = doc.getRootElement();
            resultFile = 'Root Element: '+node.getName();
            if(node.getAttributeCount() > 0) {
                for(integer i=0; i< node.getAttributeCount() ;i++) {
                    resultFile += '\nAttribute Name: '+node.getAttributeKeyAt(i)+'\nAttribute Value: '+node.getAttributeValue(node.getAttributeKeyAt(i), null);
                }
             }

            for(Dom.XmlNode xnode : node.getChildElements()) {
                resultFile +='\nElement Name:'+xnode.getName()+'\nElement Value:'+xnode.getText();
                if(xnode.getAttributeCount() > 0) {
                   for(integer i=0; i< xnode.getAttributeCount() ;i++) {               
                        resultFile += '\nAttribute Name: '+xnode.getAttributeKeyAt(i)+'\nAttribute Value: '+xnode.getAttributeValue(xnode.getAttributeKeyAt(i), null);
                   }
                }
                for(Dom.XmlNode axnode : xnode.getChildElements()) {
                    resultFile +='\nElement Name:'+axnode.getName()+'\nElement Value:'+axnode.getText();
            if(String.isNotBlank(axnode.getNamespace()))
                resultFile += '\nName Space: '+axnode.getNamespace();
            if(axnode.getAttributeCount() > 0) {
                 for(integer i=0; i< axnode.getAttributeCount() ;i++) {               
                       resultFile += '\nAttribute Name: '+axnode.getAttributeKeyAt(i)+'\nAttribute Value: '+axnode.getAttributeValue(axnode.getAttributeKeyAt(i), null);
                 }
             }
          }
     }
  }
}

Any help is appreciated.

Best Answer

You should check if the element has any value before appending it to your resultFile.

Change this: '\nElement Value:'+xnode.getText();

To: (String.isBlank(xnode.getText()) ? '' :'\nElement Value:'+xnode.getText());

So your controller would now look like this:

public class ParseTest3 {

    public String parsingXmlFile{set;get;}
    public String resultFile{set;get;}

    public void parseFile(){

        Dom.Document doc = new Dom.Document();
        doc.load(parsingXmlFile);
        Dom.XmlNode node = doc.getRootElement();
        resultFile = 'Root Element: '+node.getName();
        if(node.getAttributeCount() > 0) {
            for(integer i=0; i< node.getAttributeCount() ;i++) {
                resultFile += '\nAttribute Name: '+node.getAttributeKeyAt(i)+'\nAttribute Value: '+node.getAttributeValue(node.getAttributeKeyAt(i), null);
            }
        }

        for(Dom.XmlNode xnode : node.getChildElements()) {
            resultFile +='\nElement Name:'+xnode.getName()+(String.isBlank(xnode.getText()) ? '' :'\nElement Value:'+xnode.getText());
            if(xnode.getAttributeCount() > 0) {
                for(integer i=0; i< xnode.getAttributeCount() ;i++) {               
                    resultFile += '\nAttribute Name: '+xnode.getAttributeKeyAt(i)+'\nAttribute Value: '+xnode.getAttributeValue(xnode.getAttributeKeyAt(i), null);
                }
            }
            for(Dom.XmlNode axnode : xnode.getChildElements()) {
                resultFile +='\nElement Name:'+axnode.getName()+(String.isBlank(axnode.getText()) ? '' :'\nElement Value:'+axnode.getText());
                if(String.isNotBlank(axnode.getNamespace()))
                    resultFile += '\nName Space: '+axnode.getNamespace();
                if(axnode.getAttributeCount() > 0) {
                    for(integer i=0; i< axnode.getAttributeCount() ;i++) {               
                        resultFile += '\nAttribute Name: '+axnode.getAttributeKeyAt(i)+'\nAttribute Value: '+axnode.getAttributeValue(axnode.getAttributeKeyAt(i), null);
                    }
                }
            }
        }
    }
}

One more thing. Your current solution will work for XML files that are only 2 levels deep. I'd suggest to change it up a little bit so that it uses a recursive function:

public class ParseTest3 {

    public String parsingXmlFile{set;get;}
    public String resultFile{set;get;}

    public void parseFile(){

        Dom.Document doc = new Dom.Document();
        doc.load(parsingXmlFile);
        Dom.XmlNode node = doc.getRootElement();
        resultFile = 'Root Element: '+node.getName();
        if(node.getAttributeCount() > 0) {
            for(integer i=0; i< node.getAttributeCount() ;i++) {
                resultFile += '\nAttribute Name: '+node.getAttributeKeyAt(i)+'\nAttribute Value: '+node.getAttributeValue(node.getAttributeKeyAt(i), null);
            }
        }

        resultFile += parseRecursive(node.getChildElements());
    }

    private String parseRecursive(List<Dom.XmlNode> nodes){
        String retVal = '';

        for(Dom.XmlNode n : nodes) {
            retVal +='\nElement Name:'+n.getName()+(String.isBlank(n.getText()) ? '' : '\nElement Value:'+n.getText());
            if(String.isNotBlank(n.getNamespace()))
                retVal += '\nName Space: '+n.getNamespace();
            if(n.getAttributeCount() > 0) {
                for(integer i=0; i< n.getAttributeCount() ;i++) {               
                    retVal += '\nAttribute Name: '+n.getAttributeKeyAt(i)+'\nAttribute Value: '+n.getAttributeValue(n.getAttributeKeyAt(i), null);
                }
            }
            retVal += parseRecursive(n.getChildElements());
        }

        return retVal;
    }
}

Now your controller will be able to parse any XML string you pass to it, and you won't have to add additional nested for loops to parse additional levels of XML nodes.