So I've been developing a small LinkedHashMap extension class for use in one of my projects, where I've edited it to have a value change listener in the put
method. Heres my map class:
static class MapWithListeners<K, V> extends LinkedHashMap<K, V> {
private final LinkedHashMap<K, V> delegate;
public static final String UPDATE_EVT = "update";
public MapWithListeners() {
this.delegate = new LinkedHashMap<>();
}
public MapWithListeners(LinkedHashMap<K, V> delegate) {
this.delegate = delegate;
}
private final PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
public void addPropertyChangeListener(PropertyChangeListener listener) {
changeSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
changeSupport.removePropertyChangeListener(listener);
}
protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
changeSupport.firePropertyChange(propertyName, oldValue, newValue);
}
@Override
public V put(K var1, V var2) {
V oldValue = delegate.put(var1, var2);
firePropertyChange(UPDATE_EVT, oldValue == null ? null : new AbstractMap.SimpleEntry<>(var1, oldValue),
new AbstractMap.SimpleEntry<>(var1, var2));
return oldValue;
}
}
The problem is, im trying to map an object to an instance of this map class with:
ObjectMapper mapper = new ObjectMapper();
MapWithListeners<String, Object> map = mapper.convertValue(mainObj, new TypeReference<MapWithListeners<String, Object>>() {
});
And the result is an empty map. I've tried doing this with just a regular LinkedHashMap and it mostly works the way I need it to, but it forfeits the value change listeners, which I also need. I'm assuming I did something wrong in my MapWithListeners class, but cant figure out what that is. Thanks in advance for any help with this!
EDIT: I've found that it was necessary to change my static class to an abstract class, basically like:
abstract class MapWithListeners<K,V> extends LinkedHashMap<K,V> implements Map<K,V>
Then configure my mapper with an abstract type mapping module, like:
SimpleModule module = new SimpleModule().addAbstractTypeMapping(Map.class, MapWithListeners.class);
mapper.registerModule(module);
However, getting this far has returned an error being hit on the convertValue
line, which says:
java.lang.IllegalArgumentException: Cannot find a deserializer for non-concrete Map type [map type; class com.invoiceeditor.POJOEditor$MapWithListeners, [simple type, class java.lang.String] -> [simple type, class java.lang.Object]]
Any thoughts?
Just give the following a try: Give a little bit more type-support to the Jackson mapper by constructing a custom MapType
. Thereafter you can use it for your conversion.
I've implemented the following and it does the trick and the IllegalArgumentException
mentioned in your post is gone:
MapType javaType = mapper.getTypeFactory().constructMapType(MapWithListeners.class, String.class, Object.class);
MapWithListeners<String, Object> map = mapper.convertValue(mainObj, javaType);