97

假设我有一个非常简单的 java 对象,它只有一些 getXXX 和 setXXX 属性。此对象仅用于处理值,基本上是记录或类型安全(和高性能)映射。我经常需要将此对象转换为键值对(字符串或类型安全)或从键值对转换为此对象。

除了反射或手动编写代码来进行此转换之外,实现此目的的最佳方法是什么?

一个示例可能是通过 jms 发送此对象,而不使用 ObjectMessage 类型(或将传入消息转换为正确类型的对象)。

4

23 回答 23

186

很多潜在的解决方案,但让我们再添加一个。使用Jackson(JSON 处理库)进行“无 json”转换,例如:

ObjectMapper m = new ObjectMapper();
Map<String,Object> props = m.convertValue(myBean, Map.class);
MyBean anotherBean = m.convertValue(props, MyBean.class);

此博客条目有更多示例)

您基本上可以转换任何兼容的类型:兼容意味着如果您确实从类型转换为 JSON,并且从该 JSON 转换为结果类型,条目将匹配(如果配置正确也可以忽略无法识别的条目)。

适用于人们期望的情况,包括地图、列表、数组、原语、类 bean 的 POJO。

于 2010-01-10T23:30:06.510 回答
56

总是有 apache commons beanutils但当然它在引擎盖下使用反射

于 2009-04-11T08:30:03.377 回答
8

代码生成将是我能想到的唯一其他方式。就个人而言,我得到了一个通常可重用的反射解决方案(除非那部分代码绝对是性能关键的)。使用 JMS 听起来有点矫枉过正(额外的依赖,这甚至不是它的本意)。此外,它可能在引擎盖下也使用反射。

于 2009-04-11T07:53:13.713 回答
8

这是一种将 Java 对象转换为 Map 的方法

public static Map<String, Object> ConvertObjectToMap(Object obj) throws 
    IllegalAccessException, 
    IllegalArgumentException, 
    InvocationTargetException {
        Class<?> pomclass = obj.getClass();
        pomclass = obj.getClass();
        Method[] methods = obj.getClass().getMethods();


        Map<String, Object> map = new HashMap<String, Object>();
        for (Method m : methods) {
           if (m.getName().startsWith("get") && !m.getName().startsWith("getClass")) {
              Object value = (Object) m.invoke(obj);
              map.put(m.getName().substring(3), (Object) value);
           }
        }
    return map;
}

这是如何称呼它的

   Test test = new Test()
   Map<String, Object> map = ConvertObjectToMap(test);
于 2012-10-15T19:28:56.753 回答
7

可能迟到了。您可以使用 Jackson 并将其转换为 Properties 对象。这适用于嵌套类,如果您想要 for abc=value 中的键。

JavaPropsMapper mapper = new JavaPropsMapper();
Properties properties = mapper.writeValueAsProperties(sct);
Map<Object, Object> map = properties;

如果你想要一些后缀,那就做

SerializationConfig config = mapper.getSerializationConfig()
                .withRootName("suffix");
mapper.setConfig(config);

需要添加这个依赖

<dependency>
  <groupId>com.fasterxml.jackson.dataformat</groupId>
  <artifactId>jackson-dataformat-properties</artifactId>
</dependency>
于 2018-01-31T13:03:31.397 回答
4

使用 Java 8 你可以试试这个:

public Map<String, Object> toKeyValuePairs(Object instance) {
    return Arrays.stream(Bean.class.getDeclaredMethods())
            .collect(Collectors.toMap(
                    Method::getName,
                    m -> {
                        try {
                            Object result = m.invoke(instance);
                            return result != null ? result : "";
                        } catch (Exception e) {
                            return "";
                        }
                    }));
}
于 2015-11-04T01:56:06.947 回答
3

JSON,例如使用 XStream + Jettison,是一种带有键值对的简单文本格式。例如,Apache ActiveMQ JMS 消息代理支持它,用于与其他平台/语言进行 Java 对象交换。

于 2009-04-11T16:56:44.000 回答
3

只需使用反射和 Groovy :

def Map toMap(object) {             
return object?.properties.findAll{ (it.key != 'class') }.collectEntries {
            it.value == null || it.value instanceof Serializable ? [it.key, it.value] : [it.key,   toMap(it.value)]
    }   
}

def toObject(map, obj) {        
    map.each {
        def field = obj.class.getDeclaredField(it.key)
        if (it.value != null) {
            if (field.getType().equals(it.value.class)){
                obj."$it.key" = it.value
            }else if (it.value instanceof Map){
                def objectFieldValue = obj."$it.key"
                def fieldValue = (objectFieldValue == null) ? field.getType().newInstance() : objectFieldValue
                obj."$it.key" = toObject(it.value,fieldValue) 
            }
        }
    }
    return obj;
}
于 2012-05-13T07:55:40.963 回答
3

使用juffrou-reflect的 BeanWrapper。它非常高效。

以下是将 bean 转换为地图的方法:

public static Map<String, Object> getBeanMap(Object bean) {
    Map<String, Object> beanMap = new HashMap<String, Object>();
    BeanWrapper beanWrapper = new BeanWrapper(BeanWrapperContext.create(bean.getClass()));
    for(String propertyName : beanWrapper.getPropertyNames())
        beanMap.put(propertyName, beanWrapper.getValue(propertyName));
    return beanMap;
}

我自己开发了 Juffrou。它是开源的,因此您可以自由使用和修改。如果您对此有任何疑问,我将非常乐意回复。

干杯

卡洛斯

于 2013-06-15T16:40:42.937 回答
3

使用 Spring 时,还可以使用 Spring Integration object-to-map-transformer。可能不值得为此添加 Spring 作为依赖项。

有关文档,请在http://docs.spring.io/spring-integration/docs/4.0.4.RELEASE/reference/html/messaging-transformation-chapter.html上搜索“Object-to-Map Transformer”

本质上,它遍历作为输入的对象可到达的整个对象图,并从对象上的所有原始类型/字符串字段生成映射。它可以配置为输出:

  • 平面图:{rootObject.someField=Joe, rootObject.leafObject.someField=Jane},或
  • 结构化映射:{someField=Joe, leafObject={someField=Jane}}。

这是他们页面中的一个示例:

public class Parent{
    private Child child;
    private String name; 
    // setters and getters are omitted
}

public class Child{
   private String name; 
   private List<String> nickNames;
   // setters and getters are omitted
}

输出将是:

{person.name=乔治,person.child.name=珍娜,person.child.nickNames[0]=Bimbo。. . ETC}

也可提供反向变压器。

于 2014-10-23T08:53:29.787 回答
2

您可以使用 Joda 框架:

http://joda.sourceforge.net/

并利用 JodaProperties。但是,这确实规定您以特定方式创建 bean,并实现特定接口。但是,它确实允许您从特定类返回属性映射,而无需反射。示例代码在这里:

http://pbin.oogly.co.uk/listings/viewlistingdetail/0e78eb6c76d071b4e22bbcac748c57

于 2009-04-11T17:50:12.203 回答
2

如果您不想硬编码对每个 getter 和 setter 的调用,反射是调用这些方法的唯一方法(但这并不难)。

您可以重构相关类以使用 Properties 对象来保存实际数据,并让每个 getter 和 setter 只调用 get/set 吗?然后你就有了一个非常适合你想做的事情的结构。甚至还有以键值形式保存和加载它们的方法。

于 2009-04-14T10:31:58.643 回答
2

最好的解决方案是使用推土机。你只需要在映射器文件中这样的东西:

<mapping map-id="myTestMapping">
  <class-a>org.dozer.vo.map.SomeComplexType</class-a>
  <class-b>java.util.Map</class-b>
</mapping> 

就是这样,Dozer 负责其余的工作!!!

推土机文档 URL

于 2009-06-02T17:22:45.640 回答
2

当然有可能是最简单的转换方式 - 根本没有转换!

不要使用类中定义的私有变量,而是使类只包含一个存储实例值的 HashMap。

然后你的 getter 和 setter 返回并设置值进出 HashMap,到时候将其转换为地图,瞧!- 它已经是一张地图。

使用一点 AOP 魔法,您甚至可以通过允许您仍然使用特定于每个值名称的 getter 和 setter 来保持 bean 固有的不灵活性,而不必实际编写单独的 getter 和 setter。

于 2014-07-31T19:00:36.263 回答
2

您可以使用 java 8 流过滤器收集器属性,

public Map<String, Object> objectToMap(Object obj) {
    return Arrays.stream(YourBean.class.getDeclaredMethods())
            .filter(p -> !p.getName().startsWith("set"))
            .filter(p -> !p.getName().startsWith("getClass"))
            .filter(p -> !p.getName().startsWith("setClass"))
            .collect(Collectors.toMap(
                    d -> d.getName().substring(3),
                    m -> {
                        try {
                            Object result = m.invoke(obj);
                            return result;
                        } catch (Exception e) {
                            return "";
                        }
                    }, (p1, p2) -> p1)
            );
}
于 2016-05-04T14:46:40.630 回答
1

我的 JavaDude Bean Annotation Processor 生成代码来执行此操作。

http://javadude.googlecode.com

例如:

@Bean(
  createPropertyMap=true,
  properties={
    @Property(name="name"),
    @Property(name="phone", bound=true),
    @Property(name="friend", type=Person.class, kind=PropertyKind.LIST)
  }
)
public class Person extends PersonGen {}

上面生成的超类 PersonGen 包含一个 createPropertyMap() 方法,该方法为使用 @Bean 定义的所有属性生成一个 Map。

(请注意,我正在为下一个版本稍微更改 API —— 注释属性将为 defineCreatePropertyMap=true)

于 2009-04-13T12:00:57.707 回答
1

你应该写一个通用的转换服务!使用泛型使其保持类型自由(因此您可以将每个对象转换为 key=>value 并返回)。

哪个领域应该是关键?从 bean 中获取该字段并在值映射中附加任何其他非瞬态值。

回来的路很轻松。读取 key(x) 并首先写入 key,然后将每个列表条目写回一个新对象。

您可以使用apache commons beanutils获取 bean 的属性名称!

于 2009-04-14T11:00:41.087 回答
1

如果涉及到一个简单的对象树到键值列表的映射,其中键可能是从对象的根元素到被检查的叶子的虚线路径描述,很明显,树到键值列表的转换类似于对象到 xml 的映射。XML 文档中的每个元素都有一个定义的位置,并且可以转换为路径。因此,我将XStream作为一个基本稳定的转换工具,并用自己的实现替换了分层驱动程序和编写器部分。XStream 还带有一个基本的路径跟踪机制 - 与其他两个结合起来 - 严格地导致一个适合该任务的解决方案。

于 2011-09-22T16:45:06.857 回答
1

如果你真的想要性能,你可以走代码生成路线。

您可以通过自己的反射和构建 mixin AspectJ ITD 来实现这一点。

或者您可以使用 Spring Roo 并制作Spring Roo Addon。您的 Roo 插件将执行与上述类似的操作,但所有使用 Spring Roo 的人都可以使用,并且您不必使用运行时注释。

我都做过。人们对 Spring Roo 很感兴趣,但它确实是 Java 最全面的代码生成。

于 2012-05-31T16:39:01.623 回答
1

另一种可能的方式是在这里。

BeanWrapper 提供设置和获取属性值(单独或批量)、获取属性描述符以及查询属性以确定它们是可读还是可写的功能。

Company c = new Company();
 BeanWrapper bwComp = BeanWrapperImpl(c);
 bwComp.setPropertyValue("name", "your Company");
于 2012-07-01T19:39:49.613 回答
1

在 Jackson 库的帮助下,我能够找到 String/integer/double 类型的所有类属性,以及 Map 类中的相应值。(不使用反射 api!

TestClass testObject = new TestClass();
com.fasterxml.jackson.databind.ObjectMapper m = new com.fasterxml.jackson.databind.ObjectMapper();

Map<String,Object> props = m.convertValue(testObject, Map.class);

for(Map.Entry<String, Object> entry : props.entrySet()){
    if(entry.getValue() instanceof String || entry.getValue() instanceof Integer || entry.getValue() instanceof Double){
        System.out.println(entry.getKey() + "-->" + entry.getValue());
    }
}
于 2017-03-22T21:45:37.863 回答
0

通过使用 Gson,

  1. 将 POJO 转换object为 Json
  2. 将 Json 转换为地图

        retMap = new Gson().fromJson(new Gson().toJson(object), 
                new TypeToken<HashMap<String, Object>>() {}.getType()
        );
    
于 2018-12-11T09:28:05.833 回答
0

我们可以使用 Jackson 库轻松地将 Java 对象转换为 Map。

<dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-databind</artifactId>
   <version>2.6.3</version>
</dependency>

如果在 Android 项目中使用,您可以在应用程序的 build.gradle 中添加 jackson,如下所示:

implementation 'com.fasterxml.jackson.core:jackson-core:2.9.8'
implementation 'com.fasterxml.jackson.core:jackson-annotations:2.9.8'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.8'

示例实现

public class Employee {

    private String name;
    private int id;
    private List<String> skillSet;

    // getters setters
}

public class ObjectToMap {

 public static void main(String[] args) {

    ObjectMapper objectMapper = new ObjectMapper();

    Employee emp = new Employee();
    emp.setName("XYZ");
    emp.setId(1011);
    emp.setSkillSet(Arrays.asList("python","java"));

    // object -> Map
    Map<String, Object> map = objectMapper.convertValue(emp, 
    Map.class);
    System.out.println(map);

 }

}

输出:

{name=XYZ, id=1011, 技能=[python, java]}

于 2019-11-25T07:06:14.670 回答