1

I'm writing an REST PUT request that takes in an array of Careers and Degrees from the body and converts them to inner classes within the RestResource class. However, when I am attempting to deserialize the body string to these respected object, I get the error

Unexpected character ('(' (code 40)): expected a valid value (number, String, array, object, 'true', 'false' or 'null') at input location [1,2]

Here are my classes:

class Career {
    Integer drupalId;
    String name;
    List<Integer> degrees;

    public Career(Integer drupalId, String name, List<Integer> degrees) {
        this.drupalId = drupalId;
        this.name = name;
        this.degrees = degrees;
    }
}

class Degree {
    Integer drupalId;
    String name;

    public Degree(Integer drupalId, String name) {
        this.drupalId = drupalId;
        this.name = name;
    }
}

Here is the logic of parsing the data:

// Get Request
Map<String, Object> requestBody = (Map<String, Object>)JSON.deserializeUntyped(req.requestBody.toString());

// Parse request body
Object careerObjects = requestBody.get('careers');
Object degreeObjects = requestBody.get('degrees');

String errorString = '';
Boolean errored = false;
if (careerObjects == null) {
    errorString += '\'careers\' not provided\n';
    errored = true;
} else if (degreeObjects == null) {
    errorString += '\'degrees\' not provided\n';
    errored = true;
}
if (errored) {
    res.responseBody = Blob.valueOf(errorString);
    res.statusCode = 422;
    return;
}

String careersJsonString = String.valueOf(careerObjects); 
String degreesJsonString = String.valueOf(degreeObjects); 

System.debug(careersJsonString);
System.debug(degreesJsonString);

List<Object> careers = (List<Object>)JSON.deserialize(careersJsonString, List<Object>.class);
List<Degree> degrees =  (List<Degree>)JSON.deserialize(degreesJsonString, List<Degree>.class);

System.debug(careers);
System.debug(degrees);

Here is my Request body

{
  "careers": [
    { 
      "drupalId": 1,
      "name": "Career 1",
      "degrees": [
        1,
        2
      ]
    },
    { 
      "drupalId": 2,
      "name": "Career 2",
      "degrees": [
        2,
        3
      ]
    },
    { 
      "drupalId": 3,
      "name": "Career 3",
      "degrees": [
        1,
        3
      ]
    }
  ],
  "degrees": [
    { 
      "drupalId": 1,
      "name": "Degree 1"
    },
    { 
      "drupalId": 2,
      "name": "Degree 2"
    },
    { 
      "drupalId": 3,
      "name": "Degree 3"
    }
  ]
}

When I run this, the first two logs print out the two json parts as arrays as expected:

CareerJsonString: ({degree=(1,2), drupalId=1, name=Career 1}, { ... }, { ... }) DegreeJsonString: ({drupalId=1, name=Degree 1}, { ... }, { ... })

But then when I attempt to deserialize right after that step, it errors out with the error I explained above. How else am I suppose to deserialize these 2 objects?

1

From your 2 System.debug:

CareerJsonString: ({degree=(1,2), drupalId=1, name=Career 1}, { ... }, { ... }) 
DegreeJsonString: ({drupalId=1, name=Degree 1}, { ... }, { ... })

These 2 are not JSON. These are just Map objects printed as STRING. (No quotes saying its not JSON)

A quick fix would be, to serialize back into JSON:

String careersJsonString = JSON.serialize(careerObjects); 
String degreesJsonString = JSON.serialize(degreeObjects); 

But does it make sense to go back and forth?

A better approach will be to create a whole wrapper altogether using Json2Apex:

//
//Generated by AdminBooster
//

public class JSONOuterWrapper{
    public cls_careers[] careers;
    public cls_degrees[] degrees;
    class cls_careers {
        public Integer drupalId;    //1
        public String name; //Career 1
        public Integer [] degrees;
    }
    class cls_degrees {
        public Integer drupalId;    //1
        public String name; //Degree 1
    }
    public static JSONOuterWrapper parse(String json){

        return (JSONOuterWrapper) System.JSON.deserialize(json, JSONOuterWrapper.class);
    }


}

Test code:

String input ='{"careers":[{"drupalId":1,"name":"Career 1","degrees":[1,2]},{"drupalId":2,"name":"Career 2","degrees":[2,3]},{"drupalId":3,"name":"Career 3","degrees":[1,3]}],"degrees":[{"drupalId":1,"name":"Degree 1"},{"drupalId":2,"name":"Degree 2"},{"drupalId":3,"name":"Degree 3"}]}';

JSONOuterWrapper hw = (JSONOuterWrapper)JSON.deserialize(input, JSONOuterWrapper.class);

System.debug(hw.careers);
System.debug(hw.degrees);
  • I ended up going with 1 wrapper class overall. Thanks – BlondeSwan Jan 29 at 20:20
0

String.valueOf() returns a printable representation of its argument, but the representation is not JSON. In this case, your JSON lists have actually already been deserialized, so String.valueOf() is returning the loggable representation of a List - which is wrapped in () and is not parseable as JSON.

At this point in your code, you're finished with deserialization. What you've got to do now is cast the untyped Object value careerObjects you get back from your Map to its underlying type, which is List<Map<String, Object>>. Then you'll be able to access its components.

An alternative would be to define a strongly-typed outer class to go with your inner classes into which you can directly deserialize this message. JSON2Apex can help with this, but luckily your message is simple enough that it looks straightforward to define the message as a class and use JSON.deserialize().

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.