apply type deserialization even into maps and lists by default
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslSerializationTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslSerializationTest.java
index f331bc5..a71b79c 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslSerializationTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslSerializationTest.java
@@ -29,9 +29,12 @@
 import org.apache.brooklyn.camp.brooklyn.AbstractYamlTest;
 import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.BrooklynDslCommon;
 import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent;
+import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent.DslConfigSupplier;
 import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent.Scope;
 import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
 import org.apache.brooklyn.core.resolve.jackson.*;
+import org.apache.brooklyn.core.resolve.jackson.BrooklynJacksonSerializationUtils.ConfigurableBeanDeserializerModifier;
+import org.apache.brooklyn.core.resolve.jackson.BrooklynRegisteredTypeJacksonSerializationTest.SampleBean;
 import org.apache.brooklyn.core.resolve.jackson.WrappedValuesSerializationTest.ObjectWithWrappedValueString;
 import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.core.workflow.WorkflowBasicTest;
@@ -77,7 +80,8 @@
 
         Object stuff2 = mapper.readValue(out, Object.class);
         Object stuff2I = ((Map<?, ?>) stuff2).get("stuff");
-        Asserts.assertInstanceOf(stuff2I, Map.class);
+        Class<?> expectedDeserializedTypedMapNested = ConfigurableBeanDeserializerModifier.DEFAULT_APPLY_ONLY_TO_BEAN_DESERIALIZERS ? Map.class : DslConfigSupplier.class;
+        Asserts.assertInstanceOf(stuff2I, expectedDeserializedTypedMapNested);
     }
 
     private ObjectMapper newMapper() {
diff --git a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/BeanWithTypeUtils.java b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/BeanWithTypeUtils.java
index 2c85657..5df6c02 100644
--- a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/BeanWithTypeUtils.java
+++ b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/BeanWithTypeUtils.java
@@ -164,6 +164,7 @@
         // so the former calls the latter, and the latter calls the former. but stop at some point!
         if (stack.contains(mapOrListToSerializeThenDeserialize)) throw new IllegalStateException("Aborting recursive attempt to convert '"+mapOrListToSerializeThenDeserialize+"'");
 
+        T wrongTypeResult = null;
         try {
             stack.push(mapOrListToSerializeThenDeserialize);
 
@@ -184,14 +185,21 @@
 //                return result2;
 //            }
 
-            return result;
+            if (result!=null && !type.getRawType().isInstance(result)) {
+                wrongTypeResult = result;
+                throw new IllegalStateException("Wrong type returned");  // will be ignored below
+            } else {
+                return result;
+            }
 
         } catch (Exception e) {
             try {
                 // needed for a few things, mainly where a bean has a type field that conflicts with the type here,
-                // tryCoercer 81-wrong-bean uses this
+                // tryCoercer -20-wrong-bean uses this
                 return convertDeeply(mgmt, mapOrListToSerializeThenDeserialize, type, allowRegisteredTypes, loader, allowJavaTypes);
             } catch (Exception e2) {
+                Exceptions.propagateIfFatal(e2);
+                if (wrongTypeResult!=null) return wrongTypeResult;
                 throw Exceptions.propagate(Arrays.asList(e, e2));
             }
         } finally {
diff --git a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/BrooklynJacksonSerializationUtils.java b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/BrooklynJacksonSerializationUtils.java
index 4134a71..e957c28 100644
--- a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/BrooklynJacksonSerializationUtils.java
+++ b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/BrooklynJacksonSerializationUtils.java
@@ -28,6 +28,12 @@
 import com.fasterxml.jackson.databind.deser.DeserializerFactory;
 import com.fasterxml.jackson.databind.deser.ResolvableDeserializer;
 import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.type.ArrayType;
+import com.fasterxml.jackson.databind.type.CollectionLikeType;
+import com.fasterxml.jackson.databind.type.CollectionType;
+import com.fasterxml.jackson.databind.type.MapLikeType;
+import com.fasterxml.jackson.databind.type.MapType;
+import com.fasterxml.jackson.databind.type.ReferenceType;
 import com.fasterxml.jackson.databind.util.TokenBuffer;
 import com.google.common.annotations.Beta;
 
@@ -126,8 +132,11 @@
     }
 
     public static class ConfigurableBeanDeserializerModifier extends BeanDeserializerModifier {
+        public static boolean DEFAULT_APPLY_ONLY_TO_BEAN_DESERIALIZERS = false;
+
         List<Function<JsonDeserializer<?>,JsonDeserializer<?>>> deserializerWrappers = MutableList.of();
         List<Function<Object,Object>> postConstructFunctions = MutableList.of();
+        public boolean applyOnlyToBeanDeserializers = DEFAULT_APPLY_ONLY_TO_BEAN_DESERIALIZERS;
 
         public ConfigurableBeanDeserializerModifier addDeserializerWrapper(Function<JsonDeserializer<?>,JsonDeserializer<?>> ...f) {
             for (Function<JsonDeserializer<?>,JsonDeserializer<?>> fi: f) deserializerWrappers.add(fi);
@@ -142,6 +151,10 @@
         public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config,
                                                       BeanDescription beanDesc,
                                                       JsonDeserializer<?> deserializer) {
+            return applyOurModifiers(deserializer);
+        }
+
+        protected JsonDeserializer<?> applyOurModifiers(JsonDeserializer<?> deserializer) {
             for (Function<JsonDeserializer<?>,JsonDeserializer<?>> d: deserializerWrappers) {
                 deserializer = d.apply(deserializer);
             }
@@ -151,6 +164,56 @@
             return deserializer;
         }
 
+        private JsonDeserializer<?> applyOurModifiersNonBean(JsonDeserializer<?> deserializer) {
+            if (applyOnlyToBeanDeserializers) {
+                // all the super's callers just return the input, so it is safe to do this, ignoring the supers of the calling method
+                return deserializer;
+            }
+            return applyOurModifiers(deserializer);
+        }
+
+        @Override
+        public JsonDeserializer<?> modifyMapDeserializer(DeserializationConfig config, MapType type, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
+            return applyOurModifiersNonBean(deserializer);
+        }
+
+        @Override
+        public JsonDeserializer<?> modifyArrayDeserializer(DeserializationConfig config, ArrayType valueType, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
+            return applyOurModifiersNonBean(deserializer);
+        }
+
+        @Override
+        public JsonDeserializer<?> modifyCollectionDeserializer(DeserializationConfig config, CollectionType type, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
+            return applyOurModifiersNonBean(deserializer);
+        }
+
+        @Override
+        public JsonDeserializer<?> modifyCollectionLikeDeserializer(DeserializationConfig config, CollectionLikeType type, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
+            return applyOurModifiersNonBean(deserializer);
+        }
+
+        @Override
+        public JsonDeserializer<?> modifyEnumDeserializer(DeserializationConfig config, JavaType type, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
+            return applyOurModifiersNonBean(deserializer);
+        }
+
+        @Override
+        public JsonDeserializer<?> modifyMapLikeDeserializer(DeserializationConfig config, MapLikeType type, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
+            return applyOurModifiersNonBean(deserializer);
+        }
+
+        @Override
+        public JsonDeserializer<?> modifyReferenceDeserializer(DeserializationConfig config, ReferenceType type, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
+            return applyOurModifiersNonBean(deserializer);
+        }
+
+        // not supported at key because it is a different hierarchy; our deserializers probably don't apply; no known use case so NBD
+//        @Override
+//        public KeyDeserializer modifyKeyDeserializer(DeserializationConfig config, JavaType type, KeyDeserializer deserializer) {
+//            // super.modifyKeyDeserializer(config, type, deserializer);
+//            return applyOurModifiersNonBean(deserializer);
+//        }
+
         public <T extends ObjectMapper> T apply(T mapper) {
             SimpleModule module = new SimpleModule();
             module.setDeserializerModifier(this);
diff --git a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/ObjectReferencingSerialization.java b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/ObjectReferencingSerialization.java
index 6ad6990..8c0bfa6 100644
--- a/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/ObjectReferencingSerialization.java
+++ b/core/src/main/java/org/apache/brooklyn/core/resolve/jackson/ObjectReferencingSerialization.java
@@ -178,7 +178,7 @@
                         // and if the expected type is overly strict, return the original object.
                         // if the token buffer is used too much we might have lost the alias reference and still end up with a string,
                         // but so long as this deserializer is preferred which it normally is, losing the alias reference is okay.
-                        Maybe resultCoerced = ((Maybe) TypeCoercions.tryCoerce(result, TypeToken.of(expected)));
+                        Maybe resultCoerced = TypeCoercions.tryCoerce(result, TypeToken.of(expected));
                         if (resultCoerced.isAbsent()) {
                             // not uncommon when we are trying to deserialize in a few different ways, or if we are using a string deserializer because the json input is a string;
                             // however it can be useful, to explain why some things aren't coercing via BeanWithTypeUtils.convert.
diff --git a/core/src/test/java/org/apache/brooklyn/core/resolve/jackson/PerverseSerializationTest.java b/core/src/test/java/org/apache/brooklyn/core/resolve/jackson/PerverseSerializationTest.java
index 2c54369..cce46fa 100644
--- a/core/src/test/java/org/apache/brooklyn/core/resolve/jackson/PerverseSerializationTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/resolve/jackson/PerverseSerializationTest.java
@@ -21,6 +21,7 @@
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.common.collect.Iterables;
 import javassist.tools.rmi.Sample;
+import org.apache.brooklyn.core.resolve.jackson.BrooklynJacksonSerializationUtils.ConfigurableBeanDeserializerModifier;
 import org.apache.brooklyn.core.resolve.jackson.BrooklynRegisteredTypeJacksonSerializationTest.SampleBean;
 import org.apache.brooklyn.test.Asserts;
 import org.apache.brooklyn.util.collections.MutableList;
@@ -96,9 +97,11 @@
 
         // here, if it's an object we are reading, we get a list containing a map
         x = deser(LIST_MAP_TYPE_SAMPLE_BEAN, Object.class);
-        Asserts.assertInstanceOf(Iterables.getOnlyElement((List) x), Map.class);
+        // previously we did not deserialize types inside maps, but now we do
+        Class<?> expectedDeserializedTypedMapNested = ConfigurableBeanDeserializerModifier.DEFAULT_APPLY_ONLY_TO_BEAN_DESERIALIZERS ? Map.class : SampleBean.class;
+        Asserts.assertInstanceOf(Iterables.getOnlyElement((List) x), expectedDeserializedTypedMapNested);
 
-        // but if we know it's a list then we are more aggressive about reading type information
+        // if we know it's a list then we are more aggressive about reading type information, we do not need our bean deserializer to modify non-bean deserializers
         x = deser(LIST_MAP_TYPE_SAMPLE_BEAN, List.class);
         Asserts.assertInstanceOf(Iterables.getOnlyElement((List) x), SampleBean.class);
     }
@@ -122,8 +125,11 @@
         Asserts.assertInstanceOf(Iterables.getOnlyElement(o.ls), SampleBean.class);
         Asserts.assertInstanceOf(Iterables.getOnlyElement(o.lo), SampleBean.class);
         Asserts.assertInstanceOf(Iterables.getOnlyElement(o.lr), SampleBean.class);
-        Asserts.assertInstanceOf(Iterables.getOnlyElement((List)o.o), Map.class);
-        Asserts.assertInstanceOf(Iterables.getOnlyElement((List)Iterables.getOnlyElement(o.lo2)), Map.class);
+
+        // previously we did not deserialize types inside maps, but now we do
+        Class<?> expectedDeserializedTypedMapNested = ConfigurableBeanDeserializerModifier.DEFAULT_APPLY_ONLY_TO_BEAN_DESERIALIZERS ? Map.class : SampleBean.class;
+        Asserts.assertInstanceOf(Iterables.getOnlyElement((List)o.o), expectedDeserializedTypedMapNested);
+        Asserts.assertInstanceOf(Iterables.getOnlyElement((List)Iterables.getOnlyElement(o.lo2)), expectedDeserializedTypedMapNested);
     }
 
     /* arrays - no surprises - always read and written as lists, and coerced if context requires */