如何将Java对象(bean)转换为键值对(反之亦然)?
说我有一个非常简单的java对象,只有一些getXXX和setXXX属性。 该对象仅用于处理值,基本上是一个logging或一个types安全(和高性能)的地图。 我经常需要将该对象转换为键值对(string或types安全),或者将键值对转换为此对象。
除了reflection或手动编写代码来完成这个转换,最好的方法是什么?
一个示例可能是通过jms发送此对象,而不使用ObjectMessagetypes(或将传入消息转换为正确types的对象)。
总是有一个apache commons beanutils,但是当然它使用了引擎盖下的reflection
很多潜在的解决scheme,但是我们再添加一个。 使用jackson (JSON处理lib)来做“无json”转换,如:
ObjectMapper m = new ObjectMapper(); Map<String,Object> props = m.convertValue(myBean, Map.class); MyBean anotherBean = m.convertValue(props, MyBean.class);
( 这个博客有更多的例子)
你可以基本上转换任何兼容的types:兼容的意思,如果你从types转换为JSON,并从该JSON转换为结果types,条目将匹配(如果configuration正确也可以忽略不可识别的)。
适用于人们期望的情况,包括地图,列表,数组,基元,类似于bean的POJO。
代码生成将是我能想到的唯一的其他方式。 就个人而言,我得到了一个通常可重用的reflection解决scheme(除非这部分代码是绝对性能的关键)。 使用JMS听起来像过度杀伤(额外的依赖,这不是什么意思)。 此外,它可能也使用reflection。
这是一个将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);
JSON ,例如使用XStream + Jettison,是一个带有键值对的简单文本格式。 例如,Apache ActiveMQ JMS消息代理支持Java对象与其他平台/语言交换。
最好的解决scheme是使用推土机。 你只需要在映射文件中这样的东西:
<mapping map-id="myTestMapping"> <class-a>org.dozer.vo.map.SomeComplexType</class-a> <class-b>java.util.Map</class-b> </mapping>
就是这样,Dozer照顾其余的!
Dozer文档URL
简单地使用reflection和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; }
使用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。 它是开源的,所以你可以自由使用和修改。 如果您有任何疑问,我会很乐意回应。
干杯
卡洛斯
使用Spring时,也可以使用Spring Integration对象映射变换器。 为此,添加Spring作为依赖可能是不值得的。
有关文档,请在http://docs.spring.io/spring-integration/docs/4.0.4.RELEASE/reference/html/messaging-transformation-chapter.html上search“Object-to-Map Transformer”。
本质上,它遍历从作为input的对象到达的整个对象graphics,并从对象上的所有基本types/string字段生成一个映射。 它可以configuration为输出:
- 一个平面地图:{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] = Bimbo。 。 。 等等}
还有一个反向变压器。
对于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 ""; } })); }
你可以使用Joda框架:
并利用JodaProperties。 这确实规定了你以特定的方式创buildbean,并实现一个特定的接口。 然而,它然后,允许您从一个特定的类返回一个属性映射,没有reflection。 示例代码在这里:
http://pbin.oogly.co.uk/listings/viewlistingdetail/0e78eb6c76d071b4e22bbcac748c57
如果您不想将调用硬编码到每个getter和setter,则reflection是调用这些方法的唯一方式(但并不困难)。
你可以重构有问题的类来使用一个Properties对象来保存实际的数据,并让每个getter和setter只是调用get / set吗? 那么你有一个适合你想要做的结构。 甚至还有一些方法可以在键值forms中保存和加载它们。
当然,绝对最简单的转换方式是可能的 – 根本不需要转换!
而不是使用在类中定义的私有variables,使该类只包含一个HashMap,它存储了实例的值。
然后你的getter和setter返回并设置HashMap的值,当它是时候把它转换成地图的时候,瞧! – 它已经是一个地图。
有了一点AOP的魔力,你甚至可以保持bean固有的不灵活性,让你仍然使用特定于每个值名称的getter和setter,而不必实际编写单个的getter和setter。
我的JavaDude Bean注释处理器生成代码来执行此操作。
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)
你应该写一个通用转换服务! 使用generics来保持它的types自由(所以你可以将每个对象转换为key => value并返回)。
什么领域应该是关键? 从bean中获取该字段,并在值映射中追加其他非瞬态值。
回来的路很简单。 读键(x)并首先写入键,然后写入每个列表条目回到一个新的对象。
您可以使用apache commons beanutils获取bean的属性名称!
如果你真的想要性能,你可以去代码生成路线。
你可以通过做你自己的反思,并在AspectJ ITD中build立一个混合。
或者你可以使用Spring Roo并创build一个Spring Roo插件 。 您的Roo插件将执行与上述相似的操作,但对于使用Spring Roo的所有人都可以使用,而且您不必使用Runtime注解。
我做了两个。 人们在Spring Roo上废话,但它确实是Java最全面的代码生成。
另一种可能的方式就是在这里。
BeanWrapper提供了设置和获取属性值(单独或批量),获取属性描述符和查询属性以确定它们是否可读或可写的function。
Company c = new Company(); BeanWrapper bwComp = BeanWrapperImpl(c); bwComp.setPropertyValue("name", "your Company");
如果涉及到一个简单的对象树到键值列表映射,其中键可能是从对象的根元素到正在被检查的树叶的虚线path描述,那么相当明显的是树到键值列表的转换相当于对象到xml映射。 XML文档中的每个元素都有一个定义的位置,可以转换成一个path。 所以我把XStream作为一个基本的,稳定的转换工具,用自己的实现代替了层次化的驱动程序和写入程序部分。 XStream还带有一个基本的path跟踪机制,与其他两个机制相结合,严格地导致一个解决scheme适合于任务。
您可以使用java 8stream筛选器收集器属性,
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) ); }
在Jackson库的帮助下,我能够findString / integer / doubletypes的所有类属性以及Map类中的相应值。 ( 不使用reflectionAPI! )
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()); } }