Davyzhu Davyzhu - 3 months ago 12
Java Question

How to extract part of the *original* string from a JSON string

PLEASE, don't edit the question for me. My question is on String manipulation, changing the flow of text can very likely change the meaning of the question and cause confusion.

The problem can be viewed as a String manipulation problem. But I expect a solution in Jackson to solve my problem.

Suppose I have received a String

{"payload":{"foo":"bar","ipsum":["lorem","lorem"]},"signature":"somehmacsign"}
. When it's displayed, it's like:

{
"payload": {
"foo": "bar",
"ipsum": [
"lorem",
"lorem"
]
},
"signature": "somehmacsign"
}


How can I extract its substring from 11th character
{
till the
}
just before
,"signature"
. Namely,
{"foo":"bar","ipsum":["lorem","lorem"]}
. Semantically, I want to extract the original string representation for the
payload
field. I suppose it should not involve parsing the JSON string to Java objects and back to String. I don't want to risk losing the order of fields, spacing, or whatever small features because it's meant to be hmac signed.

Thanks in advance!

EDIT1: Rephrased to clarify that this problem has nothing to do with Java String literal.

EDIT2: Though it may be a bit early to say, there is no obvious off-the-shelf solution to my question in general (how to partial extract/parse a JSON string). In fact, I myself find my lazy/partial parsing theory a bit verbose. It requires way too many of passes to locate a deeply-nested node.

In particular for my situation, I found appending the signature in the body of a request a bad idea as it poses difficulties for the receiving party. I'm considering moving the signature to HTTP header, maybe
X-Auth-Token
?

Thanks for all that participated!

Answer

Here is a theorical solution to substract your String.

You read the JSON one character at a time and count every { you find and every }, once you have the same amount of both, you have the index of the end of the block, since you have the starting index (where you have found the first opening bracket), you can substract the String. from { to }

I will work on a basic code.

EDIT :

Here is a simple code, not create for performance or to be kept like this. Just to show that this is simple.

public static String extractFirstBlock(String s, String key){
        int length = s.length() - 1;
        int start = -1;
        int i = s.indexOf(key); //This should be done by reading characters one by one to check if this is a key or a value.

        if(i == -1) return null;

        i += key.length();

        int cntOpen = 0;
        while(i < length){
                char c = s.charAt(i);
                if(c == '{'){ //Need to check for character into String value too
                        if(cntOpen++ == 0)
                                start = i;
                } else if(c == '}'){
                        if(--cntOpen == 0){
                                return s.substring(start, i + 1);
                        }
                }
                ++i;
        }
        return null;
}

It will first search for the key you ask. Then, it will read the following string to find the block and return it. If nothing is found, null is return.

public static void main(String[] args){
    System.out.println(extractFirstBlock("{\"payday\":{\"fae\":\"boo\",\"ipsum\":[\"lom\",\"lorem\"]},\"signature\":\"somehmacsign\"},{\"payload\":{\"foo\":\"bar\",\"ipsum\":[\"lorem\",\"lorem\"]},\"signature\":\"somehmacsign\"}", "payload"));
    }

Result :

{"foo":"bar","ipsum":["lorem","lorem"]}

Improvment :

  • Keep track of where the cursor is, if in a String or not
  • Use same logic to find the key.
  • Check for escaped character