【问题标题】:Set object fields from HashMap从 HashMap 设置对象字段
【发布时间】:2012-01-17 14:06:44
【问题描述】:

是否有可以执行以下操作的库?:

给定一个 Object 和一个 HashMap,它会枚举 Hashmap 的键,并在 Object 中查找这些键的设置器并设置相关的值。看起来像这样的东西:

public Object setData(Object object, HashMap<String, Object> fields) {
   for (Entry<String, Object> entry : fields.entrySet()) {
      Method m = object.getClass().getMethod("set" + entry.getKey(), entry.getValue().getClass());
      if (m != null) {
         m.invoke(object, entry.getValue());
      }
   }
   return object;
}

乍一看,这项任务看起来很简单,但我希望有人已经注意到了一些细微差别。如您所知,重新发明轮子(好轮子)是一种不好的方法。

【问题讨论】:

    标签: java reflection pojo


    【解决方案1】:

    看看 Apache Commons BeanUtils

    org.apache.commons.beanutils.BeanUtils.populate(Object bean, Map properties)
    

    Javadoc:
    根据指定的名称/值对填充指定 bean 的 JavaBeans 属性。该方法使用 Java 反射 API 来识别对应的“属性设置器”方法名称,并处理 String、boolean、int、long、float 和 double 类型的 setter 参数。

    【讨论】:

    • 警告“仅处理 String、boolean、int、long、flat 和 double 类型的 setter 参数”非常值得注意,因此我相信以下答案(但使用 BeanUtils.copyProperty () 而不是 BeanUtils.setProperty()) 是更好的选择。
    • 请注意,如果该映射中的相应值为空,则填充方法会为某些成员设置默认值。例如。如果 。该对象具有整数(包装对象)字段,如果其在 map 中的值为 null,则将其设置为 0。如果要复制确切的值,请尝试迭代 map 并在每个字段上使用 BeanUtils.copyProperty 方法。默认情况下填充使用 setProperty 方法。
    • 使用lombok时要小心!如果您使用chain = true (@Accessors(chain=true) 生成访问器,此解决方案将无法正常工作。因为在BeanUtils 中使用的类需要设置器的无效性。请查看此问题以获取更多信息:stackoverflow.com/questions/22743765/…
    【解决方案2】:

    最好使用BeanUtils类:

    public Object setData(Object object, HashMap<String, Object> fields) {
       for(Entry<String, Object> entry : fields.entrySet()) {
          BeanUtils.setProperty(object, entry.getKey(), entry.getValue());
       }
       return object;
    }
    

    【讨论】:

    • 感谢@Wojtek O.,根据您的提示,我已经在 e-zinc 回答之前找到了填充方法!
    【解决方案3】:

    我有一个很久以前写的BeanAsMap 课程。方法asMap 返回一个Map,它是Java bean (POJO) 上的一个视图。您可以在该Map 上调用putAll,将您要从中复制数据的Map 传递给它。

    请随意使用我上面提到的代码。

    例子:

    MyClass bean = ...;
    Map<String, Object> inputData = ...;
    
    Map<String, Object> view = BeanAsMap.asMap(bean);
    view.putAll(inputData);
    

    【讨论】:

    • 您的代码很好@Jesper,但问题是Map -&gt; BeanNOT Bean -&gt; Map
    • @FelipeConde 我的代码有两种工作方式 - 您可以创建一个 bean 类的新实例,然后用 BeanAsMap 包装它并在地图上调用 put 来设置 bean 属性。如果你有一个现有的地图,你可以在包装地图上调用putAll 将现有地图的值复制到 bean。
    • @Jesper 对于简单的 JSON,您的代码可以正常工作,但在嵌套 JSON 的情况下会失败。
    【解决方案4】:

    BeanUtils 很好。

    但是,作为一种好的做法,我不会编写使用反射的代码。或者作为我拥有的最后一个解决方案,如果没有找到其他解决方案。

    这段代码不能像 Eclipse 一样在 IDE 中被跟踪(没有调用层次结构),使开发人员认为 setter 永远不会被调用。他可以破坏您的代码,并且仍然可以编译。

    这样的抽象级别太高,会使代码难以理解。

    在编写此类内容时,被混淆的代码将被混淆器本身破坏。

    最好的解决方案是重新考虑使用反射来设置对象字段。

    【讨论】:

    • 不能再同意了。
    • 虽然我同意不应该将反射用作通用工具,但偶尔也有充分的理由使用它(例如,从 JSON 或其他动态源设置 Bean 对象的属性) .
    【解决方案5】:
    猜你喜欢
    • 2016-02-03
    • 1970-01-01
    • 1970-01-01
    • 2010-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-12
    • 1970-01-01
    相关资源
    最近更新 更多