function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
TehNrdTehNrd 

Best way to generate XML string?

Sometimes API vendors don't support REST and eveyone knows the WSDL-2-Apex parser is great...when it works...which is rarely. So sometimes you have to build a good old fashion XML request from scratch. I have been wondering what is the best way to do this and I'm curious what others think. Below are some options.

Let's say we need to construct this simple XML request. To make it a little tricker there is an optional element.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sch="http://www.myservice.com/services/MyService/">
   <soapenv:Header>
      <sch:API-KEY>awefa34r32asd</sch:API-KEY>
   </soapenv:Header>
   <soapenv:Body>
      <sch:FindCompanyByKeywordRequest>
         <sch:maxRecords>20</sch:maxRecords>
         <sch:keyword>united airlines</sch:keyword>
         <sch:state>MI</sch:state> <!-- optional -->
      </sch:FindCompanyByKeywordRequest>
   </soapenv:Body>
</soapenv:Envelope>

One way to do this is with strings. Something like this:

 

String API_KEY = 'awefa34r32asd'; /*yes, this should probably be stored in custom setting but it's here to keep demo simple*/
String maxRecordsValue = '20';
String keywordValue = 'united airlines';
String stateValue = 'MI';

String req = '';
req += '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sch="http://www.myservice.com/services/MyService/">';
req +=   '<soapenv:Header>';
req +=      '<sch:API-KEY>'+API_KEY+'</sch:API-KEY>';
req +=   '</soapenv:Header>';
req +=   '<soapenv:Body>';
req +=      '<sch:FindCompanyByKeywordRequest>';
req +=         '<sch:maxRecords>'+maxRecordsValue+'</sch:maxRecords>';
req +=         '<sch:keyword>'+keywordValue+'</sch:keyword>';
if(stateValue != null){
req +=         '<sch:state>'+stateValue+'</sch:state>';
}
req +=      '</sch:FindCompanyByKeywordRequest>';
req +=   '</soapenv:Body>';
req += '</soapenv:Envelope>';

Some thoughts. It doesn't seem very elegant and feels bloated but at the same time it is super simple. You can clearly see and understand the XML so it is also sort of self documenting.


Another option is using the DOM classes and this is what it would look like:

String API_KEY = 'awefa34r32asd';
String maxRecordsValue = '20';
String keywordValue = 'united airlines';
String stateValue = 'MI';

String soapNS = 'http://schemas.xmlsoap.org/soap/envelope/';
String sch = 'http://www.myservice.com/services/MyService/';

DOM.Document doc = new DOM.Document();
dom.XmlNode envelope = doc.createRootElement('Envelope', soapNS, 'soapenv');
envelope.setNamespace('sch', sch);

//Header
dom.XmlNode header = envelope.addChildElement('Header', soapNS, null);
dom.XmlNode apikey = header.addChildElement('API-KEY', sch, null).addTextNode(API_KEY);

//Body
dom.XmlNode body = envelope.addChildElement('Body', soapNS, null);
dom.XmlNode findCompanyByKeywordRequest = body.addChildElement('FindCompanyByKeywordRequest',sch,null);
dom.XmlNode maxRecords = findCompanyByKeywordRequest.addChildElement('maxRecords', sch, null).addTextNode(maxRecordsValue);
dom.XmlNode keyword = findCompanyByKeywordRequest.addChildElement('keyword', sch, null).addTextNode(keywordValue);
if(stateValue != null){
    dom.XmlNode state = findCompanyByKeywordRequest.addChildElement('state', sch, null).addTextNode(stateValue);
}

String req = doc.toXmlString();

If feels more structured but at the same time it feels like more work. I've parsed an XML response with the DOM class but have never created an XML object so there was a bit of guess and check to get every working but it wasn't too bad once I got it all figured out.

Now I know it's not always the best idea to judge level of effort required based on the number of lines of code and characters but the DOM method is more verbose. I'm guessing some will scoff at the string approach but it was fast to implement, is easy to understand, and is self documenting.

I'm curious what other people think about these two and any other approaches to constructing an XML document?

Thanks
-Jason

 

cirruscg1cirruscg1

Honestly haven't done it. I've had to write the XML string each time, but I haven't had more than 1 or 2 opportunities since the DOM classes came out. I *think* you should be able to do it. It has namespace support, etc.

joshbirkjoshbirk

There's also the XmlStreamWriter class, which gives you a slightly different take on the second example.  I think I've ended up using all three at one point or another - and the results aren't terribly surprising.  String wrangling can be very quick, and is actually pretty easy to read as you point out - which also makes it pretty easy to debug if there's not a lot of data being sent over.

 

But - if you start needing larger and more complicated XML messages, I find that the string wrangling gets to be more and more of pain, and actually harder to debug because I also have to worry that I've put the string together correctly not just with my data, but in the correct XML format as well.   The XML classes give a bit better structure as you map it out, since you're building an object and not just a string.

 

So in the specific example you've got here, I'd be tempted to keep the first example since I can see everything that message needs to do clearly with a glance.  But if the data got larger, with more nodes, etc., I'd probably be refactoring to the second.  I've generally find there's not a lot of messaging in the middle, too - you're either sending a quick query or status to an endpoint, or you're sending a block of records.

 

HTH!

Kevin SwiggumKevin Swiggum

I've done it both ways...and I'd have to recommend the Dom.document route.

 

The main issue I've run into with String assembly is any special characters being sent...single quotes, greater/less than, etc. The Dom.document method takes care of the encoding for you to ensure you don't run into problems on the other side (i.e., can't parse the xml)....at least that's been my experience.

ca_peterson_oldca_peterson_old

My XML generation code predates the native DOM classes, so my experience is with XMLStreamWriter but it's been supprisingly un-painful to use. Serializing some fields on an sObject is only a handful of lines longer than the raw string approach. I'd vote on the DOM or XMLstreamwriter paths just because they're generally much more maintainable.

 

 

TehNrdTehNrd

The encoding issue is something I didn't consider but I think it could be taken care of with something like this. Does make it more bloated though.

 

 

req +=         '<sch:keyword>'+EncodingUtil.urlEncode(keywordValue,'UTF-8')+'</sch:keyword>';

 

 

Thanks all for the responses. I think for super simple webservices that are fairly static and where I can completely control user inputs I may go the string route. It's super quick and easy for other to understand. With that said, the majority of webservice xml requests aren't super simple and in these cases I'll be sticking with the DOM classes.

tggagnetggagne

Is there a way to add processing instructions to a DOM.Document?

 

I want to respond to user requests with XML, and specify an XSL to go with it with a processing instruct like:

 

<?xml-stylesheet href="msg.xsl" type="text/xsl"?>

 

But how do I insert that into the document so its toXmlString() will write it?

 

It looks like that feature may be available with the XmlStreamWriter class' writeProcessingInstruction() call, but it only takes two arguments so I'm unsure I could write three.