1

It is well known that it is possible to convert a JSON dictionary to an object using Apex. But as I'm trying to do so with a JSON that is returned from a webservice, I'm facing an issue while trying to convert the data to object.

The problem is that I'm having to connect to a legacy webserver that only returns XML, and I have no access to modify it. So to get it as JSON I built a webservice that acts as a bridge between Salesforce and the original webservice. Salesforce calls the bridge, the bridge calls the webservice, the webservice returns XML to the bridge, and the bridge converts the XML to JSON and returns the JSON to Salesforce.

In a certain part of my XML I get a structure like the following:

<activities>
    <activity>activity description here 1</activity>
    <activity>activity description here 2</activity>
    <activity>activity description here 3</activity>
</activities>

Seems only logical that when this is translated to JSON, it becomes something like this:

"activities": {
    "activity": [
      "activity description here 1",
      "activity description here 2",
      "activity description here 3"
    ]
  }

However, sometimes the legacy webservice might return a single "activity", like:

<activities>
    <activity>activity description here 1</activity>
</activities>

And then the JSON loses its list syntax, and becomes:

"activities" {
    "activity" : "activity description here 1"
}

Because of this, I face errors when deserializing the class. Because Salesforce doesn't know when to use a list of values or not. I created the class to deserialize this JSON to, and I cannot use List<String> activities, because it simply doesn't work when a single element is retrieved.

Has anyone faced an issue like this before? How can I solve this problem?

  • 1
    What code are you using to translate XML to JSON ? It looks problem in that .Even when a single row is returned the syntax should be an array . – Mohith Shrivastava Oct 13 '16 at 15:59
  • I'm using a little service written in Python and hosted on Heroku for that. The translation code is this simple: gist.github.com/renatoliveira/2aa37a3ea302b4792c58489540edfbd0 – Renato Oliveira Oct 13 '16 at 16:09
  • 1
    You can just make activity an Object instead of a String or List<String>. How are you consuming the data? – Adrian Larson Oct 13 '16 at 16:44
  • 1
    Yes as @AdrianLarson said you can use JSON.deserializeUntyped for this which will make it dynamic .But underlying problem is the service layer itself . – Mohith Shrivastava Oct 13 '16 at 17:02
  • I'm just sending a request and collecting the response (the webservice just returns the JSON structure as response). At first I considered parsing the XML directly, which took too much time. Now I'm trying to parse this JSON automatically, but it seems that making a custom parser using JSON.deserializeUntyped will work for me. – Renato Oliveira Oct 13 '16 at 17:18
1

Apex is a strongly-typed language and so if you have the choice, receiving a structured XML response is preferable. Using the WSDL2Apex tool (https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_callouts_wsdl2apex_gen_code.htm) with the WSDL spec of the web service, you can auto-generate classes and methods for querying the web service. Once you receive the response, you can navigate the auto-generated response object for all you need without have to do type checking. You never have to actually do any XML parsing.

If the WSDL specifies it is possible for an element to have multiple entries, the tool will automatically make it a list instead of a single value. So you'll always have reliably typed objects in the response. You end up with a class that contains a bunch of subclasses and a few method definitions. You get both synchronous and asynchronous versions. The synchronous code to use it ends up looking something like:

generatedClass.requestSubclass req = new generatedClass.requestSubclass(); 
generatedClass.responseSubclass resp = req.requestMethod(parameter1, parameter2, parameter3);

The parameters are also based on classes defined in the WSDL, so if the request has structure to it you'd send instance of those classes into the request method. And then resp contains the entire response in a structured class based on the WSDL schema. All the data inside can be retrieved with simple dot notation.

  • To begin with: I don't know how to use WSDL with Salesforce. I always found that mapping a JSON to an object was easier (there's that JSON2Apex tool too!). But now that you mentioned it, would it take care of my issue? That is, would it allow me to parse the content to a list regardless of the amount of information on that tag? – Renato Oliveira Oct 14 '16 at 20:39
  • 1
    Yes, since WSDL2Apex produces strongly typed classes, any attribute specified as a list will always return a list even if it's empty or only has one member. In the WSDL I'm working from, any element that is meant to be a list is declared using maxOccurs="unbounded" and that signals WSDL2Apex to make a list instead of a single value. You might want to give it a whirl - generating the classes is pretty easy (aside from debugging any errors in the process), and once you grasp how the generated code is structured you can use it very easily. – Charles T Oct 14 '16 at 21:40
  • 1
    Basically you end up with a class that contains a bunch of subclasses and a few method definitions. You get both synchronous and asynchronous versions. The synchronous code to use it ends up looking something like: generatedClass.Request req = new generatedClass.Request(); generatedClass.Response resp = req.Request(parameter1, parameter2, parameter3); and then resp contains the entire response in a structured class based on the WSDL schema. All the data inside can be retrieved with simple dot notation. – Charles T Oct 14 '16 at 21:48
  • I understand. Can you please edit your answer with this explanation, and with links to the WSDL2Apex tool, so I can mark it as answered? Now I'm not using your solution because I really needed to address my issue, but someone might have the time to look into this in the future. :) – Renato Oliveira Oct 14 '16 at 21:53
  • Sure, done! :-) – Charles T Oct 15 '16 at 2:14
0

I solved the problem creating a class that parses the content of my response using JSON.deserializeUntyped. When the JSON is passed to the class, I can identify the key, and detect the type of information that the value of the JSON holds. So in the code I have something like this:

if (themap.containsKey('activities')) {
    if (themap.get('activities') instanceof List<Object>) {
        // then we are dealing with a list of values
    }
    else {
        // if we get here, then we have a single element
    }
}

So I end up creating an empty list at the beginning and filling it with either the list of values or just the single value returned. As far as I've searched, this is the only way of dealing with this situation.

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.