0

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?

TheBudderBomb
  • 73
  • 1
  • 3
  • 14

1 Answers1

2

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

mle
  • 2,466
  • 1
  • 19
  • 25
  • 1
    So I actually found the correct answer to this porblem literally right after i put a bounty on it. Seems i should have just implemented map and its methods from the start and stuck to using linkedhashmapo as the delegate only. Since you were the first to comment though, have the bounty! – TheBudderBomb Feb 01 '21 at 20:52