5

JSON(I am working with): JSONLinter confirms this is a valid JSON.

{
    "errors": [
        {
            "params": {
                "password": "size must be between 4 and 30",
                "loginId": "must match \"^[a-zA-Z0-9_]*$\""
            }
        }
    ]
}

Attempt 1(Using JSONParser:

String str = '{ "errors": [ { "params": { "password": "size must be between 4 and 30", "loginId": "must match \"^[a-zA-Z0-9_]*$\"" } } ] }';
JSONParser parser = JSON.createParser(str);
while(parser.nextToken() != null){
    parser.nextToken();
}

Fails:

▸ ERROR: System.JSONException: Unexpected character ('^' (code 94)): was ▸ expecting comma to separate OBJECT entries at input location [1,99] ▸ ERROR: Class.System.JSONParser.nextToken: line 94, column 1 ▸ AnonymousBlock: line 3, column 1 ▸
AnonymousBlock: line 3, column 1

Attempt 2(JSON2Apex option):

public class JSON2Apex {

    public class Errors {
            public Params params;
    }

    public List<Errors> errors;

    public class Params {
            public String password;
            public String loginId;
    }

}

String str = '{ "errors": [ { "params": { "password": "size must be between 4 and 30", "loginId": "must match \"^[a-zA-Z0-9_]*$\"" } } ] }';
JSON2Apex obj = (JSON2Apex)JSON.deserialize(str, JSON2Apex.class);

Fails:

▸ ERROR: System.JSONException: Unexpected character ('^' (code 94)): was ▸ expecting comma to separate OBJECT entries at [line:1, column:99] ▸ ERROR: Class.System.JSON.deserialize: line 15, column 1 ▸ AnonymousBlock: line 2, column 1 ▸ AnonymousBlock: line 2, column 1

Attempt 3(deserializeUntyped):

String str = '{ "errors": [ { "params": { "password": "size must be between 4 and 30", "loginId": "must match \"^[a-zA-Z0-9_]*$\"" } } ] }';
Map<String,Object> jsonObj = (Map<String,Object>)JSON.deserializeUntyped(str);

Fails:

▸ ERROR: System.JSONException: Unexpected character ('^' (code 94)): was ▸ expecting comma to separate OBJECT entries at [line:1, column:99] ▸ ERROR: Class.System.JSON.deserializeUntyped: line 11, column 1 ▸ AnonymousBlock: line 2, column 1 ▸ AnonymousBlock: line 2, column 1

Attempt 4(replace the problem causing double quote in JSON):

String str = '{ "errors": [ { "params": { "password": "size must be between 4 and 30", "loginId": "must match \"^[a-zA-Z0-9_]*$\"" } } ] }';
Map<String,Object> jsonObj = (Map<String,Object>)JSON.deserializeUntyped(str.replaceAll('\"', '\\"'));

Still fails:

▸ ERROR: System.JSONException: Unexpected character ('^' (code 94)): was ▸ expecting comma to separate OBJECT entries at [line:1, column:99] ▸ ERROR: Class.System.JSON.deserializeUntyped: line 11, column 1 ▸ AnonymousBlock: line 2, column 1 ▸ AnonymousBlock: line 2, column 1

I have looked into the below similar questions but still don't see any solution:

  1. JSON and escaped double quote
  2. Replace double quotes with single quote in a JSON string
5
  • The answer on this one appears to me to be the correct one here. You are failing to escape a backslash in an Apex string literal - before you get into the JSON mechanics.
    – David Reed
    Jan 23, 2020 at 18:32
  • @DavidReed I am struggling to escape as suggested in that answer. What change I need to make in my replaceAll ?
    – javanoob
    Jan 23, 2020 at 18:35
  • the problem in both attempts 2 and 3 is that you are missing a \ to escape the backslash in the Apex string literal holding your JSON. It's in the source code, not the logic.
    – David Reed
    Jan 23, 2020 at 18:41
  • @DavidReed I got what you are saying but I am failing to add that extra \ to the json string before parsing. I tried doing using replaceAll but it is not doing it. If you can point what is wrong with my replaceAll (in attempt3) that would be great.
    – javanoob
    Jan 23, 2020 at 18:46
  • Is temporarily encoding the offending characters an option (like URL encode)?
    – Mast
    Jan 24, 2020 at 8:20

1 Answer 1

12

I'll try to illustrate this.

This is valid JSON.

{
    "errors": [
        {
            "params": {
                "password": "size must be between 4 and 30",
                "loginId": "must match \"^[a-zA-Z0-9_]*$\""
            }
        }
    ]
}

This is not:

String str = '{ "errors": [ { "params": { "password": "size must be between 4 and 30", "loginId": "must match \"^[a-zA-Z0-9_]*$\"" } } ] }';

Why? Because in Apex, backslash is an escape character in a string literal. If you want that backslash to carry through to the in-memory representation of the string (your JSON), you must escape it in your source code:

String str = '{ "errors": [ { "params": { "password": "size must be between 4 and 30", "loginId": "must match \\"^[a-zA-Z0-9_]*$\\"" } } ] }';

Otherwise, Apex removes it, yielding an in-memory string that is not valid JSON because nested quote marks are not escaped:

"loginId": "must match "^[a-zA-Z0-9_]*$"" 

You don't need to use replaceAll() or anything fancy to fix this (and in fact, you can't - by the time you could call replaceAll(), the string's already broken and has no backslashes in it).

The problem is not in your logic. It's just in how you're writing string literals in your source code.

4
  • If I don't use replaceAll, how can I make \" in json to \\". I understand the problem but how can I fix it at runtime?
    – javanoob
    Jan 23, 2020 at 19:14
  • You don't. The deserializer will handle it for you, so long as your remote party is sending you valid JSON. This is only a problem when you write JSON as an Apex string literal.
    – David Reed
    Jan 23, 2020 at 19:15
  • 2
    Got it. I am going to try this JSON.deserializeUntyped(httpResponse.getBody()) and see if it works. Thanks for your help.
    – javanoob
    Jan 23, 2020 at 19:18
  • 1
    This worked. If I may suggest, can you update your answer to say "just do json deserialize with raw json output from webservice without meddling it"(something like that :))
    – javanoob
    Jan 23, 2020 at 19:41

You must log in to answer this question.

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