Brian Schlenker
Brian Schlenker

Reputation: 5446

How can I convert a Map<String, String> with json values to a POJO using jackson?

I had the need to convert java objects to Map<String, String> for a REST api wrapper I am writing. Any fields that were complex objects needed to be serialized to json. I figured out how to do that like this:

public static Map<String, String> toContentMap(Object object) throws JsonProcessingException {
    Map<String, Object> objectParamMap = MAPPER.convertValue(object, new TypeReference<Map<String, Object>>() {});
    Map<String, String> contentMap = new HashMap<>();

    for (Entry<String, Object> entry : objectParamMap.entrySet()) {
        String key = entry.getKey();
        String value = MAPPER.writeValueAsString(entry.getValue());
        contentMap.put(key, StringUtils.strip(value, "\""));
    }

    return contentMap;
}

Now I need a way to get from this Map<String, String> representation back to the pojo object. Is this possible to do using mostly jackson apis?

Edit:

I guess I wasn't clear. I know the POJO I am going to/from. But it should be generic and work for any basic POJO.

An example:

class MyObject {
    String fieldA;
    Long fieldB;
    MyOtherObject fieldC;
    List<String> fieldD;
}

class MyOtherObject {
    String fieldA;
    String fieldB;
}

MyObject object = new MyObject("valueA", 20L, 
        new MyOtherObject("valueA", "valueB"), 
        Lists.newArrayList("array1", "array2"));

Map<String, String> contentMap = toContentMap(object);
/*
"fieldA" -> "valueA"
"fieldB" -> "20"
"fieldC" -> "{\"fieldA\":\"valueA\",\"fieldB\":\"valueB\"}"
"fieldD" -> "[\"array1\",\"array2\"]"
*/

MyObject newObject = fromContentMap(contentMap);
assertEquals(object, newObject)

Upvotes: 2

Views: 5766

Answers (3)

OneCricketeer
OneCricketeer

Reputation: 191963

Based on your comment

it should work for any POJO I ask it to

I think you are looking for the @JsonAnyGetter and @JsonAnySetter annotations, which will assign the values that it can to the POJO (which you need to specify at deserialization... it can't be generic), but will stick anything not parsed into a Map<String, Object>.

Note, it can't be Map<String, String> because JSON contains more than just Strings as its values.

For example, take the JSON

{
  "uid": 1,
  "username": "steve",
  "email": "[email protected]"
}

You can generate a Jackson POJO that looks like so.

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
        "uid",
        "username",
        "email"
})
public class User {

    @JsonProperty("uid")
    private Integer uid;
    @JsonProperty("username")
    private String username;
    @JsonProperty("email")
    private String email;
    @JsonIgnore
    private Map<String, Object> additionalProperties = new HashMap<String, Object>();

    @JsonProperty("uid")
    public Integer getUid() {
        return uid;
    }

    @JsonProperty("uid")
    public void setUid(Integer uid) {
        this.uid = uid;
    }

    @JsonProperty("username")
    public String getUsername() {
        return username;
    }

    @JsonProperty("username")
    public void setUsername(String username) {
        this.username = username;
    }

    @JsonProperty("email")
    public String getEmail() {
        return email;
    }

    @JsonProperty("email")
    public void setEmail(String email) {
        this.email = email;
    }

    @JsonAnyGetter
    public Map<String, Object> getAdditionalProperties() {
        return this.additionalProperties;
    }

    @JsonAnySetter
    public void setAdditionalProperty(String name, Object value) {
        this.additionalProperties.put(name, value);
    }

}

Upvotes: 2

rahulbmv
rahulbmv

Reputation: 724

You can use

ObjectMapper mapper = new ObjectMapper();
String jsonInString = "{'name' : 'rahul'}";


//JSON from String to Object
User user = mapper.readValue(jsonInString, User.class);

or

User user = mapper.convertValue(map, User.class);

if you are converting from json/map to custom object. You can also pass type information to the serialization/deserialization process so that jackson is aware of the internals. Please read more about that here.

Upvotes: 1

Darshan Mehta
Darshan Mehta

Reputation: 30839

We can't do it I am afraid. When an object gets serialized into JSON String, it loses it's class type. So, while deserializing a string into object, parser doesn't know which class to deserialize the object into. However, there are a couple of ways to do it:

Store type info in another map:

Create a new map like this:

Map<String, Class> metaData = new HashMap<>();

It will store the type name along with Class. So, while derializing, we can lookup class type from this map and pass it to readValue method.

Use @Type annotation of ObjectMapper: (only useful if all the objects extend same base class)

Jakson has a feature called Polymorphic Deserialization which will help deserializing into object if all the classes inherit common base class. Have a look at the documentation here.

Upvotes: 0

Related Questions