反射 - 如何将Java对象(bean)转换为键值对(反之亦然)?

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

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

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

Shahbaz asked 2019-09-11T04:48:31Z
22个解决方案
167 votes

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

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

(此博客条目有更多示例)

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

适用于人们期望的案例,包括地图,列表,数组,基元,类似bean的POJO。

StaxMan answered 2019-09-11T04:49:30Z
50 votes

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

Maurice Perry answered 2019-09-11T04:48:45Z
8 votes

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

Michael Borgwardt answered 2019-09-11T04:49:54Z
7 votes

这是一种将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);
DeXoN answered 2019-09-11T04:50:29Z
4 votes

使用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 "";
                        }
                    }));
}
Bax answered 2019-09-11T04:50:54Z
3 votes

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

mjn answered 2019-09-11T04:51:18Z
3 votes

最好的解决方案是使用Dozer。 你只需要在mapper文件中有这样的东西:

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

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

推土机文档URL

answered 2019-09-11T04:51:55Z
3 votes

只需使用反射和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;
}
berardino answered 2019-09-11T04:52:19Z
3 votes

使用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。 它是开源的,因此您可以自由使用它并进行修改。 如果您对此有任何疑问,我将非常乐意回复。

干杯

卡洛斯

Martins answered 2019-09-11T04:53:08Z
3 votes

使用Spring时,也可以使用Spring Integration对象到映射变换器。 可能不值得为此添加Spring作为依赖项。

有关文档,请在[http://docs.spring.io/spring-integration/docs/4.0.4.RELEASE/reference/html/messaging-transformation-chapter.html]上搜索“对象到地图变换器”。

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

  • 平面地图:{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 = George,person.child.name = Jenna,   person.child.nickNames [0] =宾博。。。 等等}

还提供反向变压器。

Jan Żankowski answered 2019-09-11T04:54:30Z
2 votes

你可以使用Joda框架:

[http://joda.sourceforge.net/]

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

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

Jon answered 2019-09-11T04:55:15Z
2 votes

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

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

Thorbjørn Ravn Andersen answered 2019-09-11T04:55:48Z
2 votes

当然,绝对最简单的转换方式是可行的 - 根本没有转换!

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

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

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

Rodney P. Barbati answered 2019-09-11T04:56:33Z
2 votes

您可以使用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)
            );
}
erhanasikoglu answered 2019-09-11T04:56:56Z
1 votes

我的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 {}

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

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

Scott Stanchfield answered 2019-09-11T04:57:41Z
1 votes

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

什么领域应该是关键? 从bean中获取该字段,并将任何其他非瞬态值附加到值映射中。

回来的路很简单。 读取密钥(x)并首先写入密钥,然后将每个列表条目写回新对象。

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

Martin K. answered 2019-09-11T04:58:26Z
1 votes

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

你可以通过自己的反思和构建一个mixJ AspectJ ITD来实现这一点。

或者你可以使用Spring Roo制作一个Spring Roo插件。 您的Roo插件将执行与上述类似的操作,但是对于使用Spring Roo且您不必使用Runtime Annotations的所有人都可以使用。

我做了两件事。 人们躲在Spring Roo上,但它确实是Java最全面的代码生成。

Adam Gent answered 2019-09-11T04:59:12Z
1 votes

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

BeanWrapper提供了设置和获取属性值的功能(单独使用)  或者批量处理),获取属性描述符,并查询属性以确定它们是否存在  是可读的还是可写的。

Company c = new Company();
 BeanWrapper bwComp = BeanWrapperImpl(c);
 bwComp.setPropertyValue("name", "your Company");
Venky answered 2019-09-11T04:59:43Z
1 votes

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

Lars Wunderlich answered 2019-09-11T05:00:09Z
1 votes

在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());
    }
}
Amit Kaneria answered 2019-09-11T05:00:34Z
1 votes

可能迟到了。 您可以使用Jackson并将其转换为Properties对象。 这适用于嵌套类,如果你想要a.b.c =值中的键。

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>
pointerness answered 2019-09-11T05:01:11Z
0 votes

通过使用Gson,

  1. 将POJO retMap = new Gson().fromJson(new Gson().toJson(object), new TypeToken<HashMap<String, Object>>() {}.getType() ); 转换为Json
  2. 将Json转换为Map

        retMap = new Gson().fromJson(new Gson().toJson(object), 
                new TypeToken<HashMap<String, Object>>() {}.getType()
        );
    
Prabs answered 2019-09-11T05:01:57Z
translate from https://stackoverflow.com:/questions/739689/how-to-convert-a-java-object-bean-to-key-value-pairs-and-vice-versa