0

I attempted to send an HTTP request and transmit a JSON to the Salesforce REST API in the following manner, but encountered the following error:

Error: [ { "message": "JSON request body must be an object at [line:1, column:2]", "errorCode": "JSON_PARSER_ERROR" } ]

I have confirmed that the message is sent in JSON format. How can I correct this error?

This is my Apex code:

@RestResource(urlMapping='/apex_rest_api_endpoint/*')
global with sharing class CreateTask {

    @HttpPost
    global static String receiveAndProcessData(String jsonBodyList) {

        Map<String, Object> requestData = (Map<String, Object>) JSON.deserializeUntyped(jsonBodyList);
        
        String lineUserId = (String) requestData.get('userId');
        String lineChannelId = (String) requestData.get('channel');

And my python code:

def get_json_msg(event, context):

    if validate_signature_taichung(event['headers']['x-line-signature'],event)==True:
                    
            data = {
                'userId': get_line_userid(event, context),
                'message': get_line_message(event, context),
                'channel': "xxxxxxx"
            }


    elif validate_signature_taipe(event['headers']['x-line-signature'],event)==True:

            data = {
                'userId': get_line_userid(event, context),
                'message': get_line_message(event, context),
                'channel': "xxxxxxx"
            }

    json_body = {"jsonBodyList": data}

    return json.dumps(json_body)

def send_msg_to_customapi(event, context):

    json_msg = get_json_msg(event, context)

    print(json_msg)

    params = {
        "grant_type": "password",
        "client_id": "xxxxxxxx", # Consumer Key
        "client_secret": "xxxxxxxx", # Consumer Secret
        "username": "xxxx@xxxxx", # The email you use to login
        "password": "xxxxxxxx" # Concat your password and your security token
    }
    r = requests.post("https://login.salesforce.com/services/oauth2/token", params=params)
    
    access_token = r.json().get("access_token")
    instance_url = r.json().get("instance_url")

    headers = {
        "Authorization": f"Bearer {access_token}",
        "Content-Type": "application/json"
    }

    # Salesforce Apex REST API URL
    api_endpoint = f"{instance_url}/services/apexrest/apex_rest_api_endpoint"

    # HTTP POST
    response = requests.post(api_endpoint, headers=headers, json=json_msg)

7
  • 1
    I don't assume this to be the reason for the error, but get_json_msg has two if-conditions and no else - how is data guaranteed to have have a value? What does print(json_msg) print? Commented Apr 28 at 8:43
  • @FelixvanHove This is because I have two Line channels, so I need to determine which Line channel the message is coming from. As a result, whenever a message is sent from either channel, I will receive the corresponding data. And when I send a message on any channel and print(json_msg), it will show information similar to the following: { "userId": "U7010c8d805c46203757281b6e8a42cbf", "message": "test", "channel": "2004037199" }
    – yukichia
    Commented Apr 28 at 9:09
  • Try to remove the parameter from the @HttpPost method and read the jsonBodyList (which is not a list/array at all) from the request: Map<String, Object> requestData = (Map<String, Object>) JSON.deserializeUntyped(RestContext.request.requestBody.toString());. Alternatively, set up an inner class with the shape of your JSON and declare this as the type of your parameter global static String receiveAndProcessData(JsonBodyList jsonBodyList).... In neither case state the parameter of the @HttpPost method as a String. Commented Apr 28 at 10:02
  • @FelixvanHove I tried to change to the method you provided, Map<String, Object> requestData = (Map<String, Object>) JSON.deserializeUntyped(RestContext.request.requestBody.toSt‌​ring());, but this time I received this error code: error: [ { "errorCode": "APEX_ERROR", "message": "System.TypeException: Invalid conversion from runtime type String to Map<String,ANY>\n\nClass.CreateTask.receiveAndProcessData: line 8, column 1" } ]
    – yukichia
    Commented Apr 28 at 13:00
  • @FelixvanHove In Python, requests.post has a json parameter--if you use it, it expects an object, not a JSON-string, so the data becomes double-encoded because of the json.dumps call. Further, the parameter was fine, but there needs to be a request wrapper, as demonstrated in my answer.
    – sfdcfox
    Commented Apr 28 at 19:24

1 Answer 1

3

When using method parameters in Apex, the parser expects a top level object that has a request element. In other words:

json_body = { 'request': { 'jsonBodyList': data } }

Further, you're double encoding the JSON, which is why you got a String data type. Do not use json.dumps:

return json_body
2
  • It works! Thank you so much!
    – yukichia
    Commented Apr 29 at 1:45
  • @yukichia Of course! Glad I could help.
    – sfdcfox
    Commented Apr 29 at 2:16

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .