79228

How to extract part of the *original* text from JSON with Jackson?

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.

<hr>

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.

<hr>

<strong>EDIT 1:</strong> Rephrased to clarify that this problem has nothing to do with Java String literal.

<strong>EDIT 2:</strong> 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?

<strong>EDIT 3:</strong> As it turns out, I was really concluding too early. Cassio's answer perfectly solves the problem by using custom deserializer and the magic skipChildren!

Answer1:

You could move the signature to a HTTP header to make things simpler.

However, if you want to keep the signature in the request payload, follow the steps described below.

Creating a custom deserializer

Create a custom deserializer that can get the raw JSON string value:

public class RawJsonDeserializer extends JsonDeserializer<String> {

    @Override
    public String deserialize(JsonParser jp, DeserializationContext ctxt)
           throws IOException, JsonProcessingException {

        long begin = jp.getCurrentLocation().getCharOffset();
        jp.skipChildren();
        long end = jp.getCurrentLocation().getCharOffset();

        String json = jp.getCurrentLocation().getSourceRef().toString();
        return json.substring((int) begin - 1, (int) end);
    }
}


Creating a Java class to hold the values

Create a Java class to hold the values (pay attention to the @JsonDeserialize annotation):

public class Request {

    @JsonDeserialize(using = RawJsonDeserializer.class)
    private String payload;

    private String signature;

    // Getters and setters ommitted
}


Parsing the JSON

Parse the JSON using ObjectMapper and Jackson will use the deserializer you have defined above:

String json = "{\"payload\":{\"foo\":\"bar\",\"ipsum\":[\"lorem\"," +
              "\"lorem\"]},\"signature\":\"somehmacsign\"}";

ObjectMapper mapper = new ObjectMapper();
Request request = mapper.readValue(json, Request.class);


If the payload is an invalid JSON, Jackson will throw an exception.

Answer2:

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 :

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

    Answer3:

    You can use simple-json.jar to extract that object from inside the JSONObject

    Downloadable Jar Link - simple-json.jar Download Link

    Maven Jar Import Maven Repository pom syntax

    You actual object is

    { "payload": { "foo": "bar", "ipsum": [ "lorem", "lorem" ] }, "signature": "somehmacsign" } // hold this complete object in any string reference Variable.

    Here I Suppose String jsonString holds the complete json object as above described.

    import org.json.simple.parser.JSONParser; import org.json.simple.parser.JSONObject; // implemetation logic for extracting jsonObject. private JSONObject extractFirstBlock(String jsonString) throws ......{ JSONObject jsonObj, desiredObject; JSONParser parser=new JSONParser(); // parser to parse string to JSONObject jsonObj = (JSONObject) parser.parse(jsonString); // parse the Object using parse Method. desiredObject = (JSONObject) jsonObj.get("payload"); // this Object contains your desired output which you wish to receive. }

    Here in desiredObject you will get your expected Values as JSONObject.

    Answer4:

    In Jackson , you will have to do something like below. It would read json as tokens and then you will need conditionals to decide what to do with the token. So here , i can go into the JSON extract the field "payload" and keep appending till i encounter "signature". Additionally the tokens can provide what type (it is an ENUM) of a "tag" it is to take an action (Start , Field , Value etc.)

    public static void main(String[] args) { StringBuffer writer = new StringBuffer(); try (FileInputStream fis = new FileInputStream("C:\\DevelopmentTools\\3.Code\\sample.json")) { boolean payloadFound = false; boolean signatureFound = false; JsonFactory jf = new JsonFactory(); JsonParser jp = jf.createParser(fis); jp.nextToken(); while (jp.hasCurrentToken()) { String text = jp.getText(); if ("payload".equals(text)) { System.out.println("payload found"); payloadFound = true; jp.nextToken(); } if ("signature".equals(text)) { System.out.println("signature found"); signatureFound = true; } if (payloadFound && !signatureFound) { } jp.nextToken(); } } catch (Exception ex) { ex.printStackTrace(); } }

Recommend

  • How to hide content not in vissible view on a page with many HTML elements to improve performance?
  • How to make the page script recognize a manually changed value of INPUT element?
  • How to find all the agents who are not included in an agentset?
  • Updating the composer.phar on Symfony2
  • Setting Unknown Array Boundaries and Loop
  • Is there a way to clear some session data from ALL sessions?
  • SVG Fill Width to Child Elements
  • How to enable Drag a Marker (Android Map Api v2) after a single Touch?
  • Moving data between processes in Spartan 3
  • fluidly dealing with multiple keypresses in ActionScript3
  • Django: DRY principle and UserPassesTestMixin
  • How to set a private variable for a jQuery plugin?
  • Drag and Drop in JList is not working
  • UIPickerView without IB?
  • Microsoft Chart Controls for Microsoft .NET Framework 4.0
  • IDX10503: Signature validation failed
  • oauth2client.client.HttpAccessTokenRefreshError: invalid_grant: Invalid JWT
  • Syntax error near unexpected token 'elif'
  • CSS how to fix an element to scroll horizontally with the page but not vertically?
  • Is there a way to call library thread-local init/cleanup on thread creation/destruction?
  • Generic/Unknown HTTP Error with response code 0 using UnityWebRequest
  • What is this strange character in chrome's resource css viewer?
  • Is there any way to call saveCurrentTurnWithMatchData without sending a push notification?
  • read values from form post in jquery or javascript
  • Django return user model id with L
  • Angular2 - Template reference inside NgSwitch
  • Laravel: Getting Session ID oddly truncates when using foreach
  • Query to find the duplicates between the name and number in table
  • Eloquent update method change created_at timestamp
  • D3 nodes and links from JSON with nested arrays of children
  • How to rebase a series of branches?
  • How to redirect a user to a different server and include HTTP basic authentication credentials?
  • Arrow is showed instead of the material design version hamburger icon. Why doesn't syncState in
  • Updated Ionic CLI but shows previous version (Windows)
  • Jquery - Jquery Wysiwyg return html as a string
  • Arrays break string types in Julia
  • WPF Applying a trigger on binding failure
  • Benchmarking RAM performance - UWP and C#
  • Java static initializers and reflection
  • MATLAB: Piecewise function in curve fitting toolbox using fittype