2

I'm trying to get my head around the convertValue method of Jackson. Initially I thought it would be somewhat equivalent to the Gson#fromJson method but this doesn't seem to be the case.

Here's the problem:

// Map<String, Object> map = ...
ObjectMapper mapper = new ObjectMapper();
MyPojo pojo = mapper.convertValue(map.get(key), MyPojo.class);
for (Map.Entry<String, Object> m : map.entrySet()) {
    System.out.println("k=" + m.getKey() + "    v=" + m.getValue());
}
pojo.name = "Banana";
for (Map.Entry<String, Object> m : map.entrySet()) {
    System.out.println("k=" + m.getKey() + "    v=" + m.getValue());
}

Output

k=66e8c013   v=MyPojo{name='Apple'} 
k=66e8c013   v=MyPojo{name='Banana'}

Note: Code and output has been stripped down to the relevant part

So, if I modify my pojo object, it's also getting changed in the original Map. For me it seems like Jackson doesn't call "new MyPojo()" internally and set the values for each found variable afterwards.

What can I do to prevent this? Is there an alternative method? Do I need to create a copy constructor in order to get a truly new object without references to the values in the Map?
Additionally it would be cool if someone could tell me what convertValue actually does.

user3420815
  • 775
  • 9
  • 20
  • 1
    So, you're trying to convert a MyPojo to MyPojo? Jackson won't do anything when you do that: it will just return the original MyPojo, because there is no need to convert anything. – JB Nizet Jan 24 '16 at 13:56
  • The source is always your best source for what code does. –  Jan 24 '16 at 14:03
  • @JBNizet Not necessarily. Often times it's a `LinkedHashMap` and in this case the error didn't occur. Though looking at the debugger you're right in this special case - so thanks. – user3420815 Jan 24 '16 at 18:18
  • @JarrodRoberson I don't know why you've closed this as a duplicate? I haven't asked *"how to clone an object"* - I'm totally aware of my options. Initially I've tried to understand what's happening in my code. So closing doesn't seem appropriate - no offense. – user3420815 Jan 24 '16 at 18:19

1 Answers1

0

Looking into the code of convertValue, you find

    if (targetType != Object.class
            && !toValueType.hasGenericTypes()
            && targetType.isAssignableFrom(fromValue.getClass())) {
        return fromValue;
    }

So in your case if map.get(key) is of type MyPojo, the convertValue() method indeed returns the reference to your input. This explains, why the value in the map changes when you change the 'converted' object.

Now your question can be deduced to how to clone an object, which is a complicated topic in general.

  • If you want safe deep copy, you can serialize and deserialize the object. This procedure, however, gives bad performance.

  • You could implement the clone() method and mark the class Cloneable, which is a rather common solution and gives you control and good performance.

  • You can also look into Apache Commons BeanUtils which has the cloneBean() method which uses getters and setters to clone the properties of the object.

David Frank
  • 5,918
  • 8
  • 28
  • 43