4

I want to convert a complex json string–which I get from a GET request–to our database. I need to loop through all the objects and find some specific ones. The problem is that all objects are different in some way. They could look like these three examples, but there are many more:

{
  "created": 1493209170473990,
  "id": "fu:9843464EDF4D053072ACEAC2362EE0D8",
  "type": "user-created"
},
{
  "created": 1493209170883075,
  "data": {
    "process_type": "wallet-tx"
  },
  "id": "fu:6BE085BF29D7C8AF4C238615CA85F31A",
  "process": "0CEB2F401E0FB9D9A44A124D0710B521",
  "type": "process-created"
},
{
  "created": 1495535185484487,
  "data": {
    "message": "electronic delivery"
  },
  "document": "25FBED0A80FEEBD6FF154D21D8E35D7E",
  "id": "fu:3C17584381C0AFB4836F73057DB7DEAB",
  "type": "info"
}

I need to find some objects with a specific type, but I cant get them out of a string. I get the request data with this call:

@RequestMapping(value="/events", method=RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
@CrossOrigin(origins = "http://localhost:4200", maxAge = 3600)
public String getEvents() {

    int created_after = 0;

    final String url = server + "/rest/client/events/" + created_after;

    RestTemplate restTemplate = new RestTemplate();
    restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));
    HttpHeaders headers = new HttpHeaders();
    headers.add("Content-Type", "application/json; charset=utf-8");
    headers.set("Auth-Token", token); // user_token

    HttpEntity<String> entity = new HttpEntity<String>("parameters", headers);

    ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);

    return response.getBody();
}

I use Angular in my frontend, which could easy convert the string to an Object, but then I have to pass this again to my backend to work with the data. I want to keep everything in the backend. Do you have any idea how to solve it?

If you need more information, please ask. Thanks

EDIT: My JSON output looks like this:

[
  {
    "created": 1493209170473990,
    "id": "fu:9843464EDF4D053072ACEAC2362EE0D8",
    "type": "user-created"
  },
  {
    "created": 1493209170653925,
    "data": {
      "verify_id": "12581C42DD2DF7D80F802C50ABD144F8"
    },
    "id": "fu:06111B0A9C5B760B9269044DA97D3D6F",
    "type": "post-address-verification-confirmed"
  },
  {
    "created": 1493209171320041,
    "data": {
      "after": {
        "attempt": 1
      }
    },
    "id": "fu:7B5B2AD57C1CE97BB642931C2C3C819D",
    "process": "0CEB2F401E0FB9D9A44A124D0710B521",
    "type": "process-updated"
  },
...
]
0

2 Answers 2

7

The way I understand it, your objects have some common properties, as well as some optional ones. You can model the optional properties using @JsonAnyGetter and @JsonAnySetter:

class Data {
    @JsonProperty
    private Long created;
    @JsonProperty
    private String id;
    @JsonProperty
    private String type;
    private Map<String, Object> optional = new HashMap<>();
    public Data() { // empty public constructor is required
    }
    // getters/setters for all properties omitted for brevity
    @JsonAnySetter
    public void addOptional(String name, Object value) {
        optional.put(name, value);
    }
    @JsonAnyGetter
    public Object getOptional(String name) {
        return optional.get(name);
    }
}

Then you can deserialize your objects using

ObjectMapper objectMapper = new ObjectMapper();
Data data = objectMapper.readValue(j, Data.class);

or, if you've got an array of your Data objects as input,

Data[] data = objectMapper.readValue(j, Data[].class);

All properties except created, id and type will be placed in the optional map.

Sign up to request clarification or add additional context in comments.

6 Comments

I also tried your answer, bur I also got an error: Can not deserialize instance of abc.model.Event out of START_ARRAY token Maybe i implemented it wrong? Unfortunatly i'm new to Java
I tried abit and now the error is gone, but I dont get the optional data as output. only ID, created and type. I used the data class as you provided. I changed Data[] data = objectMapper.readValue(j, Data[].class);
The error about START_ARRAY token is because I thought you'd deserialize objects individually, not as an array. If it's an array of objects you've got, then yes, you deserialize them using Data[] data = objectMapper.readValue(json, Data[].class);. The extra properties should be deserialized as Map values for the optional property. BTW, how did you check that you don't have the optional data elements? For the first array element, it's going to be empty, but for the other two, there should be something inside
Also make sure all your imports come from the same package root. I know the Jackson has been moved between org.codehouse.jackson and con.fasterxml.jackson packages, so if your project somehow references both (e.g., pulled as a Maven dependency), make sure to not mix them up.
@Peter No, there can be only one @JsonAnyGetter/Setter per class, and it acts as a catch-all sink for any properties you haven't defined. So process and document would end up in the optional property as well, when present. Then again, nothing prevents you from declaring them as @JsonProperty instead, then they would end up deserialized as a regular property. By default Jackson treats all @JsonProperty as optional, so they would be null when not present. @JsonAnySetter is just a safe way to make sure you've captured everything, even if some properties were missed in the analysis
|
6

If you don't know what the structure of the JSON is going to be then you can serialize your JSON string to a Map which maps the field names in the json to their value.

This can be done using the Jackson ObjectMapper:

String jsonObject = <the string JSON response you obtained>;

ObjectMapper objectMapper = new ObjectMapper();
Map<String, Object> jsonMap = objectMapper.readValue(jsonString,
    new TypeReference<Map<String, Object>>(){});

If it's a list of JSON objects you expect then you can first map this to an array of JSON strings and then convert each one to a map:

ObjectMapper objectMapper = new ObjectMapper();
String[] jsonStrings = objectMapper.readValue(jsonString, String[]);
List<Map<String, Object>> jsonMaps = new ArrayList<>();
for (String json : jsonStrings) {
    jsonMaps.add(objectMapper.readValue(json, 
            new TypeReference<Map<String, Object>>(){});
}

4 Comments

thx for the reply. If I use this, i get following exception: Can not deserialize instance of java.util.LinkedHashMap out of START_ARRAY token. Can't find any solution for it
Whats the exact JSON String that is causing this exception?
i will post the json output in my original article above
Ah ok the problem is your JSON is coming back as a list of objects. I was under the impression it was just a single JSON object with unknown structure. I'll update my answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.