302

I've been looking at Jackson, but is seems I would have to convert the Map to JSON, and then the resulting JSON to the POJO.

Is there a way to convert a Map directly to a POJO?

Marco
  • 1,377
  • 15
  • 18
user86834
  • 5,357
  • 10
  • 34
  • 47

9 Answers9

548

Well, you can achieve that with Jackson, too. (and it seems to be more comfortable since you were considering using jackson).

Use ObjectMapper's convertValue method:

final ObjectMapper mapper = new ObjectMapper(); // jackson's objectmapper
final MyPojo pojo = mapper.convertValue(map, MyPojo.class);

No need to convert into JSON string or something else; direct conversion does much faster.

Jongwook Choi
  • 8,171
  • 3
  • 25
  • 22
  • 12
    You need to include this library to use ObjectMapper `compile 'com.fasterxml.jackson.core:jackson-databind:2.7.3'` – Shajeel Afzal Dec 25 '16 at 20:31
  • 12
    Using convertValue is the right answer, but don't create an ObjectMapper instance every time. It's expensive to create and thread-safe, so create one and cache it somewhere. – glade Feb 26 '17 at 23:06
  • 1
    do you know how to do the opposite - or how to convert an object to a Map? – fIwJlxSzApHEZIl Jul 11 '17 at 18:10
  • 3
    @RaduSimionescu did you figure out how to deep-convert objects with nested maps / lists to a `Map` instance? – fIwJlxSzApHEZIl Jul 11 '17 at 18:13
  • @anon58192932 it works if you follow this answer. I was just dealing with some weird objects which were modeling lists as maps and when serializing was getting unexpected results. but that was another issue, nothing to do with jackson – Radu Simionescu Jul 12 '17 at 20:20
  • 1
    What about datatypes? Does it handle Long to int (for example)? – markthegrea May 28 '20 at 18:01
  • You can also convert a Map to a POJO using `jackson`'s `ObjectMapper`. All you have to take care is the `Object` stored in the map is an underlying basic Java data types like `int`, `Integer`, `long`, `Long`, `String`, `List` etc. – Binita Bharati Sep 08 '21 at 05:20
84

A solution with Gson:

Gson gson = new Gson();
JsonElement jsonElement = gson.toJsonTree(map);
MyPojo pojo = gson.fromJson(jsonElement, MyPojo.class);
Sergey Morozov
  • 4,528
  • 3
  • 25
  • 39
AlikElzin-kilaka
  • 34,335
  • 35
  • 194
  • 277
15
ObjectMapper objectMapper = new ObjectMapper();
// Use this if all properties are not in the class
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
final MyPojo pojo = objectMapper.convertValue(map, MyPojo.class);

Same as the first answer but I got an error using that because I don't want all properties of the Map converted to the class. I also found objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); as the solution.

Westy92
  • 19,087
  • 4
  • 72
  • 54
Shafeeq Mohammed
  • 1,193
  • 17
  • 25
10

if you have generic types in your class you should use TypeReference with convertValue().

final ObjectMapper mapper = new ObjectMapper();
final MyPojo<MyGenericType> pojo = mapper.convertValue(map, new TypeReference<MyPojo<MyGenericType>>() {});

Also you can use that to convert a pojo to java.util.Map back.

final ObjectMapper mapper = new ObjectMapper();
final Map<String, Object> map = mapper.convertValue(pojo, new TypeReference<Map<String, Object>>() {});
bhdrk
  • 3,415
  • 26
  • 20
  • when using convertValue to map a Map to a pojo, how to deal with the case when the Map contains fields which are not present in the dto , if the fields are same, it works, however if there is one more field in the map than the dto, then it throws IllegalArgumentException, how to handle this case, any ideas or lead ? – Gurkirat Singh Guliani Jun 30 '21 at 04:49
  • @GurkiratSinghGuliani did you try `@JsonIgnoreProperties(ignoreUnknown = true)` ? – bhdrk Jun 30 '21 at 09:19
  • hey , got it figured using ```ObjectMapper objMapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); ``` – Gurkirat Singh Guliani Jun 30 '21 at 09:34
5

Yes, its definitely possible to avoid the intermediate conversion to JSON. Using a deep-copy tool like Dozer you can convert the map directly to a POJO. Here is a simplistic example:

Example POJO:

public class MyPojo implements Serializable {
    private static final long serialVersionUID = 1L;

    private String id;
    private String name;
    private Integer age;
    private Double savings;

    public MyPojo() {
        super();
    }

    // Getters/setters

    @Override
    public String toString() {
        return String.format(
                "MyPojo[id = %s, name = %s, age = %s, savings = %s]", getId(),
                getName(), getAge(), getSavings());
    }
}

Sample conversion code:

public class CopyTest {
    @Test
    public void testCopyMapToPOJO() throws Exception {
        final Map<String, String> map = new HashMap<String, String>(4);
        map.put("id", "5");
        map.put("name", "Bob");
        map.put("age", "23");
        map.put("savings", "2500.39");
        map.put("extra", "foo");

        final DozerBeanMapper mapper = new DozerBeanMapper();
        final MyPojo pojo = mapper.map(map, MyPojo.class);
        System.out.println(pojo);
    }
}

Output:

MyPojo[id = 5, name = Bob, age = 23, savings = 2500.39]

Note: If you change your source map to a Map<String, Object> then you can copy over arbitrarily deep nested properties (with Map<String, String> you only get one level).

Perception
  • 79,279
  • 19
  • 185
  • 195
  • 1
    How could you do a "deep copy" from Map to POJO? Say for example you have a User.class which encapsulates an Address.class and the map has a key like "address.city", "address.zip" and these need to map to User.Address.City and User.Address.Zip? It doesn't seem to automatically interpret the dot in the Map key as a sub-level to the object graph. – szxnyc Aug 07 '15 at 21:51
5

I have tested both Jackson and BeanUtils and found out that BeanUtils is much faster.
In my machine(Windows8.1 , JDK1.7) I got this result.

BeanUtils t2-t1 = 286
Jackson t2-t1 = 2203


public class MainMapToPOJO {

public static final int LOOP_MAX_COUNT = 1000;

public static void main(String[] args) {
    Map<String, Object> map = new HashMap<>();
    map.put("success", true);
    map.put("data", "testString");

    runBeanUtilsPopulate(map);

    runJacksonMapper(map);
}

private static void runBeanUtilsPopulate(Map<String, Object> map) {
    long t1 = System.currentTimeMillis();
    for (int i = 0; i < LOOP_MAX_COUNT; i++) {
        try {
            TestClass bean = new TestClass();
            BeanUtils.populate(bean, map);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
    long t2 = System.currentTimeMillis();
    System.out.println("BeanUtils t2-t1 = " + String.valueOf(t2 - t1));
}

private static void runJacksonMapper(Map<String, Object> map) {
    long t1 = System.currentTimeMillis();
    for (int i = 0; i < LOOP_MAX_COUNT; i++) {
        ObjectMapper mapper = new ObjectMapper();
        TestClass testClass = mapper.convertValue(map, TestClass.class);
    }
    long t2 = System.currentTimeMillis();
    System.out.println("Jackson t2-t1 = " + String.valueOf(t2 - t1));
}}
Hamedz
  • 726
  • 15
  • 27
  • 6
    The difference is: Jackson has a whole type conversion framework with it. e.g. the `Map` contains `map.put("data","2016-06-26")` and `TestClass` has a field `private LocalDate data;`, then Jackson would be able to get things done, while BeanUtils will fail. – Benjamin M Jun 26 '16 at 10:00
  • 7
    I heard that creating `ObjectMapper` instance is a time/resources consuming process, and it's recommended to re-use one mapper instance instead of creating it anew each time. I think it would be better to take it out from the test lop – Mixaz May 12 '18 at 13:07
  • 4
    not a fair test, since BeanUtils is able to cache after the first iteration, whereas ObjectMapper is never given the chance. – Lucas Ross Aug 31 '18 at 03:55
3

The answers provided so far using Jackson are so good, but still you could have a util function to help you convert different POJOs as follows:

    public static <T> T convert(Map<String, Object> aMap, Class<T> t) {
        try {
            return objectMapper
                    .convertValue(aMap, objectMapper.getTypeFactory().constructType(t));
        } catch (Exception e) {
            log.error("converting failed! aMap: {}, class: {}", getJsonString(aMap), t.getClass().getSimpleName(), e);
        }
        return null;
    }
Hearen
  • 7,420
  • 4
  • 53
  • 63
  • 2
    I know it's an off-topic comment, but I think it is a bad idea to ignore exceptions. Therefore I do not see any value of this utility function beyond `objectMapper.convertValue`. – Jongwook Choi Dec 06 '20 at 19:23
2

convert Map to POJO example.Notice the Map key contains underline and field variable is hump.

User.class POJO

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;

@Data
public class User {
    @JsonProperty("user_name")
    private String userName;
    @JsonProperty("pass_word")
    private String passWord;
}

The App.class test the example

import java.util.HashMap;
import java.util.Map;

import com.fasterxml.jackson.databind.ObjectMapper;

public class App {
    public static void main(String[] args) {
        Map<String, String> info = new HashMap<>();
        info.put("user_name", "Q10Viking");
        info.put("pass_word", "123456");

        ObjectMapper mapper = new ObjectMapper();
        User user = mapper.convertValue(info, User.class);

        System.out.println("-------------------------------");
        System.out.println(user);
    }
}
/**output
-------------------------------
User(userName=Q10Viking, passWord=123456)
 */
Q10Viking
  • 982
  • 8
  • 9
1

@Hamedz if use many data, use Jackson to convert light data, use apache... TestCase:



import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;

import com.fasterxml.jackson.databind.ObjectMapper;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

public class TestPerf {

    public static final int LOOP_MAX_COUNT = 1000;

    public static void main(String[] args) {
        Map<String, Object> map = new HashMap<>();
        map.put("success", true);
        map.put("number", 1000);
        map.put("longer", 1000L);
        map.put("doubler", 1000D);
        map.put("data1", "testString");
        map.put("data2", "testString");
        map.put("data3", "testString");
        map.put("data4", "testString");
        map.put("data5", "testString");
        map.put("data6", "testString");
        map.put("data7", "testString");
        map.put("data8", "testString");
        map.put("data9", "testString");
        map.put("data10", "testString");

        runBeanUtilsPopulate(map);

        runJacksonMapper(map);
    }

    private static void runBeanUtilsPopulate(Map<String, Object> map) {
        long t1 = System.currentTimeMillis();
        for (int i = 0; i < LOOP_MAX_COUNT; i++) {
            try {
                TestClass bean = new TestClass();
                BeanUtils.populate(bean, map);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
        long t2 = System.currentTimeMillis();
        System.out.println("BeanUtils t2-t1 = " + String.valueOf(t2 - t1));
    }

    private static void runJacksonMapper(Map<String, Object> map) {
        long t1 = System.currentTimeMillis();
        for (int i = 0; i < LOOP_MAX_COUNT; i++) {
            ObjectMapper mapper = new ObjectMapper();
            TestClass testClass = mapper.convertValue(map, TestClass.class);
        }
        long t2 = System.currentTimeMillis();
        System.out.println("Jackson t2-t1 = " + String.valueOf(t2 - t1));
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class TestClass {
        private Boolean success;
        private Integer number;
        private Long longer;
        private Double doubler;
        private String data1;
        private String data2;
        private String data3;
        private String data4;
        private String data5;
        private String data6;
        private String data7;
        private String data8;
        private String data9;
        private String data10;

    }
}