【问题标题】:Convert a Map<String, String> to a POJO将 Map<String, String> 转换为 POJO
【发布时间】:2013-05-01 23:36:15
【问题描述】:

我一直在查看 Jackson,但似乎我必须将 Map 转换为 JSON,然后将生成的 JSON 转换为 POJO。

有没有办法将 Map 直接转换为 POJO?

【问题讨论】:

    标签: json dictionary jackson pojo


    【解决方案1】:

    嗯,您也可以通过 Jackson 实现这一目标。 (因为您考虑使用杰克逊,它似乎更舒服)。

    使用ObjectMapperconvertValue方法:

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

    无需转换成JSON字符串或其他;直接转换的速度要快得多。

    【讨论】:

    • 你需要包含这个库才能使用 ObjectMapper compile 'com.fasterxml.jackson.core:jackson-databind:2.7.3'
    • 使用 convertValue 是正确的答案,但不要每次都创建 ObjectMapper 实例。创建和线程安全的成本很高,因此创建一个并将其缓存在某处。
    • 你知道如何做相反的事情 - 或者如何将对象转换为 Map
    • @RaduSimionescu 你知道如何将带有嵌套地图/列表的对象深度转换为Map&lt;String, Object&gt; 实例吗?
    • @anon58192932 如果您遵循此答案,它将起作用。我只是在处理一些奇怪的对象,这些对象将列表建模为地图,并且当序列化得到意想不到的结果时。但那是另一个问题,与杰克逊无关
    【解决方案2】:

    Gson的解决方案:

    Gson gson = new Gson();
    JsonElement jsonElement = gson.toJsonTree(map);
    MyPojo pojo = gson.fromJson(jsonElement, MyPojo.class);
    

    【讨论】:

    • 反之亦然
    • @Prabs - 反之亦然 gson.toJson()
    • 无需将map转json。 map.toString() 就足够了。 Gson gson = 新 Gson(); MyPojo pojo = gson.fromJson(map.toString(), MyPojo.class);
    • @Esakkiappan.E,您为什么认为map.toString() 会提供正确的字符串? toString() 的实现不保证特定格式。
    【解决方案3】:

    如果你的类中有泛型类型,你应该使用TypeReferenceconvertValue()

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

    您也可以使用它来将 pojo 转换为 java.util.Map 返回。

    final ObjectMapper mapper = new ObjectMapper();
    final Map<String, Object> map = mapper.convertValue(pojo, new TypeReference<Map<String, Object>>() {});
    

    【讨论】:

    • 使用 convertValue 将 Map 映射到 pojo 时,如何处理 Map 包含 dto 中不存在的字段的情况,如果字段相同,它可以工作,但是如果地图中的字段比 dto 多一个,那么它会抛出 IllegalArgumentException,如何处理这种情况,有什么想法或线索?
    • @GurkiratSinghGuliani 你试过@JsonIgnoreProperties(ignoreUnknown = true) 吗?
    • 嘿,用ObjectMapper objMapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 搞定了
    【解决方案4】:

    是的,绝对可以避免中间转换为 JSON。使用像 Dozer 这样的深拷贝工具,您可以将地图直接转换为 POJO。这是一个简单的例子:

    示例 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());
        }
    }
    

    示例转换代码:

    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);
        }
    }
    

    输出:

    MyPojo[id = 5,姓名 = Bob,年龄 = 23,储蓄 = 2500.39]

    注意:如果您将源映射更改为Map&lt;String, Object&gt;,那么您可以复制任意深度的嵌套属性(使用Map&lt;String, String&gt;,您只能获得一层)。

    【讨论】:

    • 如何从 Map 到 POJO 进行“深拷贝”?比如说你有一个 User.class 封装了一个 Address.class 并且地图有一个像“address.city”,“address.zip”这样的键,这些需要映射到 User.Address.City 和 User.Address.Zip ?它似乎不会自动将 Map 键中的点解释为对象图的子级别。
    【解决方案5】:
     ObjectMapper objectMapper = new ObjectMapper();
     //if all properties are not in class use this
     objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
     final MyPojo pojo =     objectMapper.convertValue(map, MyPojo.class);
    

    与第一个答案相同,但我使用它时出错,因为我不希望地图的所有属性都转换为 calss。我找到 objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);这是解决方案

    【讨论】:

      【解决方案6】:

      我测试了 Jackson 和 BeanUtils,发现 BeanUtils 更快。
      在我的机器上(Windows8.1,JDK1.7)我得到了这个结果。

      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));
      }}
      

      【讨论】:

      • 不同的是:Jackson 有一个完整的类型转换框架。例如Map 包含 map.put("data","2016-06-26")TestClass 有一个字段 private LocalDate data;,那么 Jackson 将能够完成任务,而 BeanUtils 将失败。
      • 听说创建ObjectMapper 实例是一个耗费时间/资源的过程,建议重复使用一个映射器实例,而不是每次都重新创建它。我认为最好把它从测试 lop 中取出来
      • 不是一个公平的测试,因为 BeanUtils 能够在第一次迭代后缓存,而 ObjectMapper 从来没有机会。
      【解决方案7】:

      到目前为止使用 Jackson 提供的答案非常好,但您仍然可以使用 util 函数来帮助您转换不同的POJOs,如下所示:

          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;
          }
      

      【讨论】:

      • 我知道这是一个离题的评论,但我认为忽略异常是个坏主意。因此,除了objectMapper.convertValue,我看不到这个实用函数的任何价值。
      【解决方案8】:

      将 Map 转换为 POJO 示例。注意 Map 键包含下划线,字段变量是驼峰。

      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;
      }
      

      App.class 测试示例

      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)
       */
      

      【讨论】:

        【解决方案9】:

        @Hamedz 如果使用大量数据,请使用 Jackson 转换 轻数据,使用 apache... 测试用例:

        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; } }

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-07-29
          • 2013-05-24
          • 2014-01-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-07-21
          相关资源
          最近更新 更多