[SalesForce] Best Practices for generating large XML files

I currently have some requirements for an integration that I am doing and was curious if anyone had some feedback on the best initial approach. I am currently working on building out the structure using the Dom Document method for generating the file but I am looking for some guidance on the most expandable direction. They use the ACORD standard for interpreting XML documents.

I definitely see that I can use custom settings to essentially build out the structure making it changeable from an admin or someone else who is technically inclined but I am not convinced it is completely necessary.

The document I am making is very large and has some repeatable sections in it. Currently, I am making data members in a controller to populate the values of the various elements. The larger elements have their own methods to encapsulate the fields required to build them but essentially one method will construct the entire string based on populated values (data members). Obviously this can be bad if XML tags change and the order changed and this would be better to correct if it was all contained in a custom setting but are there any other things that I could potentially hit following this approach?

Example code below:

 Dom.XMLnode parentNode=  rootNode.addChildElement('ParentNodeName',null,null);

        createComplexElement(parentNode);

        parentNode.addChildElement('SimpleElement1',null,null).addTextNode(this.SimpleElement1Text);
        parentNode.addChildElement('SimpleElement2',null,null).addTextNode(this.SimpleElement2Text);

        createADifferentComplexElement(parentNode);

The code above is getting larger as the sample XML document I have has over 200 lines of tags. The size of the file can be variable based on various conditions that could make it much larger depending on how many repeatable section there are for any given scenario.

I still need to verify with the 3rd party system if I need to worry about XML tags being changed or modified but I feel like I should handle this situation anyways.

If anyone has some experience dealing with creating large XML documents – any information you can discuss on what you hit down the road and possible suggestions to avoid that scenario would be greatly appreciated!

Thank you.

If the question is too ambiguous – I can refine it.

Best Answer

I frequently work with integrations that use JSON as well as integrations that use XML. I much prefer JSON because of the native serialization/deserialization that is offered by APEX, but I have developed a similar strategy for working with XML that has made things much smoother for me.

For starters, I prepare a class to represent the endpoint data the same as I would for JSON. This class has primitive variables to represent all of the XML nodes that I am interested in. For a sample address service, this might look like:

public class AddressService {

  public class AddressValidationRequest {

    public UserDetails userDetails {get;set;}
    public Address address {get;set;}

    public AddressValidationRequest(Account acct) {
      userDetails = new UserDetails();
      address = new Address(acct);
    }

    public String toXml() {

      // Create our top level doc and request node
      Dom.Document requestDoc = createXMLRequestDocument();
      Dom.XmlNode bodyNode = requestDoc.getRootElement().addChildElement('soapenv:Body', null, null);
      Dom.XmlNode requestMessageNode = bodyNode.addChildElement('addressValidationRequest', null, null);

      // Add user details
      Dom.XmlNode userDetailsNode = requestMessageNode.addChildElement('userDetails', null, null);
      userDetails.addToXmlDoc(userDetailsNode);

      // Add address
      Dom.XmlNode addressNode = requestMessageNode.addChildElement('address', null, null);
      address.addToXmlDoc(addressNode);

      return requestDoc.toXMLString();
    }
  }

  public class UserDetails {

    public String username {get;set;}
    public String accessToken {get;set;}

    public UserDetails() {
      // In an actual app these would probably come from a custom setting
      username = 'me@company.com';
      accessToken = 'kjhgjkshfgfkjdfhg';
    }

    public void addToXmlDoc(Dom.XmlNode parentNode) {
      parentNode.addChildElement('username', null, null)
        .addTextNode(username);
      parentNode.addChildElement('accessToken', null, null)
        .addTextNode(accessToken);
    }
  }

  public class Address {

    public String streetAddress {get;set;}
    public String city {get;set;}
    // ... other address vars

    public Address(Account acct) {
      this.streetAddress = acc.ShippingStreet;
      this.city = acc.ShippingCity;
      // ... set other address vars
    }

    public void addToXmlDoc(Dom.XmlNode parentNode) {
      parentNode.addChildElement('streetAddress', null, null)
        .addTextNode(streetAddress);
      parentNode.addChildElement('city', null, null)
        .addTextNode(city);
      // ... add other vars
    }
  }
}

This then allows you to construct a new instance of your class, then call instance.toXML() to generate an XML request string. This keeps all details of how the XML is constructed abstracted away in this class, so that when we implement another service with the same pattern the developer working on it can simply make a new instance of the class and call .toXML() again without needing to know exactly how the XML is constructed.

The class can then be expanded for consuming the response with a result subclass:

public class AddressService {

  public class AddressValidationResult {
    Boolean isValid {get;set;}
    String message {get;set;}

    public AddressValidationResult(Dom.XmlNode parentNode) {
      for(Dom.XmlNode parentNode_child : doc.getRootElement().getChildElements()) {
        if(parentNode_child.getName() == 'isValid') {
          isValid = Boolean.valueOf(parentNode_child.getText());
        }
        else if(parentNode_child.getName() == 'message') {
          message = parentNode_child.getText();
        }
      }
    }
  }
}

This allows you to easily construct the AddressService response from the result of an HTTP call.

This pattern heavily promotes re-use of sections without having to write much new code. Have multiple addresses in your request? Simply add another Address variable in the top level request class, construct it in your request constructor, and use .addToXmlDoc to place it in your document. This pattern has also worked well for us when working with XML that has very deeply nested nodes.

I am not sure if this is exactly what you are looking for but hopefully it provokes some new thought on how to approach XML generation in Apex.