Deserialize Nested Json Record From Postgres To POJO using Jackson

Question:

I am preety new to Jackson and how it really works under the hood when its get complicated to an issue of what i am facing. I have a record coming from the database which is stored as JSON TYPE (using postgres). Below is a sample of how it is in the database:
{"flutterwave": {"secret": "SECRET KEYS"}, "dlocal": {"xkey": X KEY VALUE", "xlogin": "X LOGIN VALUE"}}

Coming from python world, i would have just done json.loads(DATA_FROM_DB_IN_JSON) and it automatically converts the resulting output to a Dictionary in which i can easily retrieve and utilize the keys as i want them, However with Jackson library of Java, i haven’t been able to get it to work.

Below is what i have done in java and haven’t gotten it to work the way i would have expected it if were python.

public class PaymentConfigDTO {

    @JsonAlias({"secrets"})
    @JsonDeserialize(using = KeepAsJsonDeserializer.class)
    private String processorCredentials;
}

DESERIALIZER CLASS

public class KeepAsJsonDeserializer extends JsonDeserializer<String> {
    @Override
    public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException {
        TreeNode tree = jsonParser.getCodec().readTree(jsonParser);
        return tree.toString();
    }
}

In summary, what i want to acheive is been able to convert the result json coming from the db to deserializable to a Map<String, Map<>> or a better approach where i will be able to get the nested values without much stress.

Asked By: Oluwaseun Peter

||

Answers:

If you want to get a Map<String, Map<String, String>> you can do:

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
...
var mapper = new ObjectMapper();
var result = mapper.readValue(json, new TypeReference<Map<String, Map<String, String>>>() {});

But would be better to use types:

record Config(Flutterwave flutterwave, DLocal dlocal) {}
record Flutterwave(String secret) {}
record DLocal(String xkey, String xlogin) {}
...
var mapper = new ObjectMapper();
var result = mapper.readValue(json, Config.class);
var secret = result.flutterwave().secret()
Answered By: Lae

Assuming that you already have Json String it is very simple to deserialize it to Map<String, Object>. you don’t need to write your own deserializer class. All you need to do is:

ObjectMapper om = new ObjectMapper();
try {
  Map<String, Object> map = om.readValue(jsonStr, Map.class);
} catch(IOException ioe) {
...
}

See JavaDoc for ObjectMapper
Also, if you want it even simpler I wrote my own JsonUtil where you don’t even have to instantiate ObjectMapper. Your code would look like this:

 try {
    Map<String, Object> map = JsonUtils.readObjectFromJsonString(jsonStr, Map.class);
} catch (IOException ioe) {
    ...
}

In this example class JsonUtils comes with Open Source MgntUtils library written and maintained by me. See the Javadoc for JsonUtils class. The MgntUtils library can be obtained from Maven Central as Maven artifact or from Github along with Source code and Javadoc

Answered By: Michael Gantman

So what i did basically was to update the custom deserializer class to use a JsonNode instead of TreeNode

It was re-written as:

@Override
    public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException {
        JsonNode node = jsonParser.getCodec().readTree(jsonParser);
        return node.get("value").textValue();
    }

Then to convert to a map, i did
Map<String, Object> toMap = objectMapper.readValue(JSON, Map.class)

Answered By: Oluwaseun Peter