103

Say I have a very simple java object that only has some getXXX and setXXX properties. This object is used only to handle values, basically a record or a type-safe (and performant) map. I often need to covert this object to key value pairs (either strings or type safe) or convert from key value pairs to this object.

Other than reflection or manually writing code to do this conversion, what is the best way to achieve this?

An example might be sending this object over jms, without using the ObjectMessage type (or converting an incoming message to the right kind of object).

M-Razavi
  • 3,327
  • 2
  • 34
  • 46
Shahbaz
  • 10,395
  • 21
  • 54
  • 83
  • [`java.beans.Introspector`](https://docs.oracle.com/javase/8/docs/api/java/beans/Introspector.html)`.getBeanInfo()`. It's built right into the JDK. – user207421 Nov 25 '19 at 07:20

24 Answers24

192

Lots of potential solutions, but let's add just one more. Use Jackson (JSON processing lib) to do "json-less" conversion, like:

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

(this blog entry has some more examples)

You can basically convert any compatible types: compatible meaning that if you did convert from type to JSON, and from that JSON to result type, entries would match (if configured properly can also just ignore unrecognized ones).

Works well for cases one would expect, including Maps, Lists, arrays, primitives, bean-like POJOs.

vbence
  • 20,084
  • 9
  • 69
  • 118
StaxMan
  • 113,358
  • 34
  • 211
  • 239
57

There is always apache commons beanutils but of course it uses reflection under the hood

Maurice Perry
  • 32,610
  • 9
  • 70
  • 97
  • 9
    Besides, there is nothing wrong about reflection being used, under the hood. Especially if results are cached (for future use), reflection-based access is usually fast enough for most use cases. – StaxMan Mar 19 '10 at 03:59
  • 26
    To be precise, BeanMap(bean) is the specific part of common beanutils that does the trick. – vdr Apr 22 '13 at 09:49
  • 5
    Disregarding performance considerations I've found that using BeanMap() in combination with Jackson's ObjectMapper from the answer below gave me the best results. BeanMap succeeded at creating a more complete map of my object and then Jackson converted it into plain LinkedHashMap structure which allowed modifications further down the pipeline. objectAsMap = objectMapper.convertValue(new BeanMap(myObject), Map.class); – michaelr524 Nov 21 '14 at 11:06
  • Won't work on Android because it uses `java.beans` dependencies severely limited on the platform. See the [related question](https://stackoverflow.com/questions/15065545/using-jars-that-use-java-beans-classes-introspector-beaninfo-or-propertydescri) for details. – SqueezyMo Jul 06 '17 at 14:28
  • A solution mentioning Apache Commons is most of the time the best solution. – рüффп Apr 12 '18 at 10:44
8

This is a method for converting a Java object to a 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;
}

This is how to call it

   Test test = new Test()
   Map<String, Object> map = ConvertObjectToMap(test);
DeXoN
  • 91
  • 1
  • 6
  • 1
    I don't think the desired answer iterates over the methods defined on each object, sounds like reflection. – Robert Karl Oct 15 '12 at 19:36
  • 3
    I don't know your purposes is your method. But i think this is very excellent example when you want to programming with generic types of objects – DeXoN Oct 15 '12 at 19:38
8

Code generation would be the only other way I can think of. Personally, I'd got with a generally reusable reflection solution (unless that part of the code is absolutely performance-critical). Using JMS sounds like overkill (additional dependency, and that's not even what it's meant for). Besides, it probably uses reflection as well under the hood.

Michael Borgwardt
  • 342,105
  • 78
  • 482
  • 720
  • Performance is critical so I think reflection is out. I was hoping that there might be some tools that use asm or cglib. I have started looking at Google's protocol buffers again. I didn't get your comment about JMS, I am using it to communicate information across machines. – Shahbaz Apr 11 '09 at 19:01
  • I misunderstood that. As for performance, there's a difference between performance being important and that specific part being performance-critical. I'd do a benchmark to see how much it really matters compared to what else the application is doing. – Michael Borgwardt Apr 11 '09 at 20:48
  • That's also my thought. Either you use reflection (directly or indirectly) or you have to generate code. (Or write the extra code yourself, of course). – extraneon Aug 18 '10 at 12:07
7

Probably late to the party. You can use Jackson and convert it into a Properties object. This is suited for Nested classes and if you want the key in the for a.b.c=value.

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

if you want some suffix, then just do

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

need to add this dependency

<dependency>
  <groupId>com.fasterxml.jackson.dataformat</groupId>
  <artifactId>jackson-dataformat-properties</artifactId>
</dependency>
pointerness
  • 323
  • 3
  • 12
4

When using Spring, one can also use Spring Integration object-to-map-transformer. It's probably not worth adding Spring as a dependency just for this.

For documentation, search for "Object-to-Map Transformer" on http://docs.spring.io/spring-integration/docs/4.0.4.RELEASE/reference/html/messaging-transformation-chapter.html

Essentially, it traverses the entire object graph reachable from the object given as input, and produces a map from all primitive type/String fields on the objects. It can be configured to output either:

  • a flat map: {rootObject.someField=Joe, rootObject.leafObject.someField=Jane}, or
  • a structured map: {someField=Joe, leafObject={someField=Jane}}.

Here's an example from their page:

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
}

Output will be:

{person.name=George, person.child.name=Jenna, person.child.nickNames[0]=Bimbo . . . etc}

A reverse transformer is also available.

Jan Żankowski
  • 8,690
  • 7
  • 38
  • 52
4

With Java 8 you may try this :

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
  • 4,260
  • 5
  • 43
  • 65
3

Simply using reflection and 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;
}
Reuse3733
  • 150
  • 2
  • 5
3

Use juffrou-reflect's BeanWrapper. It is very performant.

Here is how you can transform a bean into a map:

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

I developed Juffrou myself. It's open source, so you are free to use it and modify. And if you have any questions regarding it, I'll be more than happy to respond.

Cheers

Carlos

Community
  • 1
  • 1
Martins
  • 1,231
  • 2
  • 14
  • 17
  • I thought about it and realized that my earlier solution breaks if you have circular references in you bean graph. So I developed a method that will do that transparently and handles circular references beautifully. You can now convert a bean into a map and vice-versa with juffrou-reflect. Enjoy :) – Martins Jun 16 '13 at 16:24
3

You can use the java 8 stream filter collector properties,

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
  • 1,685
  • 1
  • 21
  • 33
3

JSON, for example using XStream + Jettison, is a simple text format with key value pairs. It is supported for example by the Apache ActiveMQ JMS message broker for Java object exchange with other platforms / languages.

mjn
  • 36,362
  • 28
  • 176
  • 378
2

There is of course the absolute simplest means of conversion possible - no conversion at all!

instead of using private variables defined in the class, make the class contain only a HashMap which stores the values for the instance.

Then your getters and setters return and set values into and out of the HashMap, and when it is time to convert it to a map, voila! - it is already a map.

With a little bit of AOP wizardry, you could even maintain the inflexibility inherent in a bean by allowing you to still use getters and setters specific to each values name, without having to actually write the individual getters and setters.

Rodney P. Barbati
  • 1,883
  • 24
  • 18
2

You could use the Joda framework:

http://joda.sourceforge.net/

and take advantage of JodaProperties. This does stipulate that you create beans in a particular way however, and implement a specific interface. It does then however, allow you to return a property map from a specific class, without reflection. Sample code is here:

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

Jonathan Holloway
  • 62,090
  • 32
  • 125
  • 150
  • Looks interesting, unfortunately it doesn't look like it is maintained any more. Besides, the property map it returns is a map of , so not type safe. – Shahbaz Apr 11 '09 at 18:57
  • 1
    True, it hasn't been maintained since 2002. I was just curious as to whether a non-reflection based solution existed. The property map it returns is actually just a standard map, no generics as such... – Jonathan Holloway Apr 11 '09 at 22:55
2

If you do not want to hardcode calls to each getter and setter, reflection is the only way to call these methods (but it is not hard).

Can you refactor the class in question to use a Properties object to hold the actual data, and let each getter and setter just call get/set on it? Then you have a structure well suited for what you want to do. There is even methods to save and load them in the key-value form.

Thorbjørn Ravn Andersen
  • 73,784
  • 33
  • 194
  • 347
  • There are no methods, because it depends on you what is a key and what is value! It should be easy to write a service which does such a conversion. For a simple representation CSV might be acceptable. – Martin K. Apr 14 '09 at 11:02
2

Add Jackson library

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

Then you could generate map using Object mapper.

ObjectMapper objectMapper = new ObjectMapper();

Map<String, Object> map = objectMapper.convertValue(
    yourJavaObject, 
    new TypeReference<Map<String, Object>>() {}
);
Priyatham
  • 135
  • 9
2

The best solution is to use Dozer. You just need something like this in the mapper file:

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

And that's it, Dozer takes care of the rest!!!

Dozer Documentation URL

  • Why does it require mapping file? If it knows how to convert, why require this extra work -- just take source object, expected type? – StaxMan Mar 19 '10 at 03:58
  • Ok. It's good to know the reason, even though I'm not sure it's valid :) – StaxMan Oct 15 '10 at 18:03
1

If you really really want performance you can go the code generation route.

You can do this on your on by doing your own reflection and building a mixin AspectJ ITD.

Or you can use Spring Roo and make a Spring Roo Addon. Your Roo addon will do something similar to the above but will be available to everyone who uses Spring Roo and you don't have to use Runtime Annotations.

I have done both. People crap on Spring Roo but it really is the most comprehensive code generation for Java.

Adam Gent
  • 47,843
  • 23
  • 153
  • 203
1

One other possible way is here.

The BeanWrapper offers functionality to set and get property values (individually or in bulk), get property descriptors, and to query properties to determine if they are readable or writable.

Company c = new Company();
 BeanWrapper bwComp = BeanWrapperImpl(c);
 bwComp.setPropertyValue("name", "your Company");
Brad Larson
  • 170,088
  • 45
  • 397
  • 571
Venky
  • 85
  • 1
1

With the help of Jackson library, I was able to find all class properties of type String/integer/double, and respective values in a Map class. (without using reflections 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
  • 5,466
  • 2
  • 35
  • 38
1

My JavaDude Bean Annotation Processor generates code to do this.

http://javadude.googlecode.com

For example:

@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 {}

The above generates superclass PersonGen that includes a createPropertyMap() method that generates a Map for all properties defined using @Bean.

(Note that I'm changing the API slightly for the next version -- the annotation attribute will be defineCreatePropertyMap=true)

Scott Stanchfield
  • 29,742
  • 9
  • 47
  • 65
  • Have you done code-reviews? This doensn't seem to be a good approach! You change the inheritance path with annotations! What if the bean extends already at a class?! Why have you to write the attributes twice? – Martin K. Apr 14 '09 at 11:42
  • If you already have a superclass, you use @Bean(superclass=XXX.class, ...) and it inserts the generated superclass inbetween. This has been great for code reviews -- much less potentially-error-prone boilerplate to sift though. – Scott Stanchfield Apr 14 '09 at 14:08
  • Not sure what you mean by "write the attributes twice" -- where do they show up twice? – Scott Stanchfield Apr 14 '09 at 14:09
  • @Martin K. If you really don't want to use reflection, even under the hood, then you're probably forced to do some kind of code generation. – extraneon Aug 18 '10 at 12:06
1

You should write a generic transformation Service! Use generics to keep it type free (so you can convert every object to key=>value and back).

What field should be the key? Get that field from the bean and append any other non transient value in a value map.

The way back is pretty easy. Read key(x) and write at first the key and then every list entry back to a new object.

You can get the property names of a bean with the apache commons beanutils!

Martin K.
  • 4,669
  • 7
  • 35
  • 49
1

If it comes to a simple object tree to key value list mapping, where key might be a dotted path description from the object's root element to the leaf being inspected, it's rather obvious that a tree conversion to a key-value list is comparable to an object to xml mapping. Each element within an XML document has a defined position and can be converted into a path. Therefore I took XStream as a basic and stable conversion tool and replaced the hierarchical driver and writer parts with an own implementation. XStream also comes with a basic path tracking mechanism which - being combined with the other two - strictly leads to a solution being suitable for the task.

alestanis
  • 21,519
  • 4
  • 48
  • 67
0

By using Gson,

  1. Convert POJO object to Json
  2. Convert Json to Map

        retMap = new Gson().fromJson(new Gson().toJson(object), 
                new TypeToken<HashMap<String, Object>>() {}.getType()
        );
    
Prabs
  • 4,923
  • 6
  • 38
  • 59
  • Hi, What if I have structure like ===> corporate:{ id:1; company:{id=1, member: {id:1}} } Can i get map like all the unique key and value – Biyu Nov 25 '20 at 14:22
  • is this your json string? `{"corporate":{"id":1,"company":{"id":1,"member":{"id":1}}}}` – Prabs Nov 28 '20 at 07:49
  • @LokeshKumar what's your Json? Post a new question and give me the link. I'll try to help. – Prabs Nov 28 '20 at 07:50
0

We can use the Jackson library to convert a Java object into a Map easily.

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

If using in an Android project, you can add jackson in your app's build.gradle as follows:

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'

Sample Implementation

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

 }

}

Output:

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

Anubhav
  • 1,984
  • 22
  • 17