Repository: johnzon Updated Branches: refs/heads/master a7c03892d -> 2507062b0
JOHNZON-197 Support johnzon.interfaceImplementationMapping jsonb property (and mapper option) Project: http://git-wip-us.apache.org/repos/asf/johnzon/repo Commit: http://git-wip-us.apache.org/repos/asf/johnzon/commit/2507062b Tree: http://git-wip-us.apache.org/repos/asf/johnzon/tree/2507062b Diff: http://git-wip-us.apache.org/repos/asf/johnzon/diff/2507062b Branch: refs/heads/master Commit: 2507062b09ab6deaa5b05cc21f4854e61e6a1092 Parents: a7c0389 Author: Romain Manni-Bucau <[email protected]> Authored: Mon Dec 17 17:36:48 2018 +0100 Committer: Romain Manni-Bucau <[email protected]> Committed: Mon Dec 17 17:36:48 2018 +0100 ---------------------------------------------------------------------- .../jaxrs/ConfigurableJohnzonProvider.java | 45 +++++++++++----- .../jaxrs/jsonb/jaxrs/JsonbJaxrsProvider.java | 17 ++++++ .../apache/johnzon/jsonb/JohnzonBuilder.java | 3 ++ .../johnzon/jsonb/InterfaceMappingTest.java | 57 ++++++++++++++++++++ .../apache/johnzon/mapper/MapperBuilder.java | 9 +++- .../org/apache/johnzon/mapper/MapperConfig.java | 8 ++- .../org/apache/johnzon/mapper/Mappings.java | 17 ++++-- .../apache/johnzon/mapper/MapperConfigTest.java | 4 +- .../java/org/superbiz/ExtendMappingTest.java | 11 ++-- src/site/markdown/index.md | 6 ++- 10 files changed, 148 insertions(+), 29 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/johnzon/blob/2507062b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/ConfigurableJohnzonProvider.java ---------------------------------------------------------------------- diff --git a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/ConfigurableJohnzonProvider.java b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/ConfigurableJohnzonProvider.java index 30a452d..0d34696 100644 --- a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/ConfigurableJohnzonProvider.java +++ b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/ConfigurableJohnzonProvider.java @@ -18,20 +18,9 @@ */ package org.apache.johnzon.jaxrs; -import org.apache.johnzon.mapper.MapperBuilder; -import org.apache.johnzon.mapper.SerializeValueFilter; -import org.apache.johnzon.mapper.access.AccessMode; -import org.apache.johnzon.mapper.access.BaseAccessMode; +import static java.util.Arrays.asList; +import static java.util.stream.Collectors.toMap; -import javax.json.JsonReaderFactory; -import javax.json.stream.JsonGeneratorFactory; -import javax.ws.rs.Consumes; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.ext.MessageBodyReader; -import javax.ws.rs.ext.MessageBodyWriter; -import javax.ws.rs.ext.Provider; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -40,9 +29,24 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Type; import java.util.Comparator; import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; -import static java.util.Arrays.asList; +import javax.json.JsonReaderFactory; +import javax.json.stream.JsonGeneratorFactory; +import javax.ws.rs.Consumes; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyReader; +import javax.ws.rs.ext.MessageBodyWriter; +import javax.ws.rs.ext.Provider; + +import org.apache.johnzon.mapper.MapperBuilder; +import org.apache.johnzon.mapper.SerializeValueFilter; +import org.apache.johnzon.mapper.access.AccessMode; +import org.apache.johnzon.mapper.access.BaseAccessMode; @Provider @Produces("application/json") @@ -155,6 +159,19 @@ public class ConfigurableJohnzonProvider<T> implements MessageBodyWriter<T>, Mes builder.setAccessModeFieldFilteringStrategy(strategy); } + public void setInterfaceImplementationMapping(final Map<String, String> interfaceImplementationMapping) { + final ClassLoader loader = Thread.currentThread().getContextClassLoader(); + final Function<String, Class<?>> load = name -> { + try { + return loader.loadClass(name.trim()); + } catch (final ClassNotFoundException e) { + throw new IllegalArgumentException(e); + } + }; + builder.setInterfaceImplementationMapping(interfaceImplementationMapping.entrySet().stream() + .collect(toMap(it -> load.apply(it.getKey()), it -> load.apply(it.getValue())))); + } + public void setAccessModeFieldFilteringStrategyName(final String mode) { builder.setAccessModeFieldFilteringStrategyName(mode); } http://git-wip-us.apache.org/repos/asf/johnzon/blob/2507062b/johnzon-jsonb/src/main/java/org/apache/johnzon/jaxrs/jsonb/jaxrs/JsonbJaxrsProvider.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jaxrs/jsonb/jaxrs/JsonbJaxrsProvider.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jaxrs/jsonb/jaxrs/JsonbJaxrsProvider.java index 25cf998..b59b697 100644 --- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jaxrs/jsonb/jaxrs/JsonbJaxrsProvider.java +++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jaxrs/jsonb/jaxrs/JsonbJaxrsProvider.java @@ -18,6 +18,8 @@ */ package org.apache.johnzon.jaxrs.jsonb.jaxrs; +import static java.util.stream.Collectors.toMap; + import javax.json.JsonStructure; import javax.json.bind.Jsonb; import javax.json.bind.JsonbBuilder; @@ -41,6 +43,7 @@ import java.io.Writer; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.Collection; +import java.util.Map; import java.util.Properties; import java.util.function.Function; import java.util.logging.Logger; @@ -130,6 +133,20 @@ public class JsonbJaxrsProvider<T> implements MessageBodyWriter<T>, MessageBodyR customized = true; } + public void setInterfaceImplementationMapping(final Map<String, String> interfaceImplementationMapping) { + final ClassLoader loader = Thread.currentThread().getContextClassLoader(); + final Function<String, Class<?>> load = name -> { + try { + return loader.loadClass(name.trim()); + } catch (final ClassNotFoundException e) { + throw new IllegalArgumentException(e); + } + }; + config.setProperty("johnzon.interfaceImplementationMapping", interfaceImplementationMapping.entrySet().stream() + .collect(toMap(it -> load.apply(it.getKey()), it -> load.apply(it.getValue())))); + customized = true; + } + // actual impl @Override http://git-wip-us.apache.org/repos/asf/johnzon/blob/2507062b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java index 50be3ed..e533923 100644 --- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java +++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java @@ -214,6 +214,9 @@ public class JohnzonBuilder implements JsonbBuilder { config.getProperty("johnzon.deduplicateObjects") .map(v -> !Boolean.class.isInstance(v) ? Boolean.parseBoolean(v.toString()) : Boolean.class.cast(v)) .ifPresent(builder::setDeduplicateObjects); + config.getProperty("johnzon.interfaceImplementationMapping") + .map(Map.class::cast) + .ifPresent(builder::setInterfaceImplementationMapping); final Map<AdapterKey, Adapter<?, ?>> defaultConverters = createJava8Converters(builder); http://git-wip-us.apache.org/repos/asf/johnzon/blob/2507062b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/InterfaceMappingTest.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/InterfaceMappingTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/InterfaceMappingTest.java new file mode 100644 index 0000000..c5bdc4e --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/InterfaceMappingTest.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.johnzon.jsonb; + +import static java.util.Collections.singletonMap; +import static org.junit.Assert.assertEquals; + +import javax.json.bind.Jsonb; +import javax.json.bind.JsonbBuilder; +import javax.json.bind.JsonbConfig; + +import org.junit.Test; + +public class InterfaceMappingTest { + @Test + public void mapInterface() throws Exception { + try (final Jsonb jsonb = JsonbBuilder.create(new JsonbConfig() + .setProperty("johnzon.interfaceImplementationMapping", singletonMap(SomeItf.class, SomeImpl.class)))) { + assertEquals("ok", jsonb.fromJson("{\"value\":\"ok\"}", SomeItf.class).getValue()); + } + } + + public interface SomeItf { + String getValue(); + void setValue(String value); + } + + public static class SomeImpl implements SomeItf { + private String value; + + @Override + public String getValue() { + return value; + } + + @Override + public void setValue(final String value) { + this.value = value; + } + } +} http://git-wip-us.apache.org/repos/asf/johnzon/blob/2507062b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java ---------------------------------------------------------------------- diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java index 126ff0a..5d6debb 100644 --- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java +++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java @@ -132,6 +132,7 @@ public class MapperBuilder { private Map<Class<?>, ObjectConverter.Reader<?>> objectConverterReaders = new HashMap<Class<?>, ObjectConverter.Reader<?>>(); private Map<Class<?>, ObjectConverter.Writer<?>> objectConverterWriters = new HashMap<Class<?>, ObjectConverter.Writer<?>>(); private Map<Class<?>, String[]> ignoredForFields = new HashMap<Class<?>, String[]>(); + private Map<Class<?>, Class<?>> interfaceImplementationMapping = new HashMap<>(); private BaseAccessMode.FieldFilteringStrategy fieldFilteringStrategy = null; private boolean primitiveConverters; private boolean failOnUnknownProperties; @@ -239,10 +240,16 @@ public class MapperBuilder { skipNull, skipEmptyArray, treatByteArrayAsBase64, treatByteArrayAsBase64URL, readAttributeBeforeWrite, accessMode, encoding, attributeOrder, enforceQuoteString, failOnUnknownProperties, - serializeValueFilter, useBigDecimalForFloats, deduplicateObjects), + serializeValueFilter, useBigDecimalForFloats, deduplicateObjects, + interfaceImplementationMapping), closeables); } + public MapperBuilder setInterfaceImplementationMapping(final Map<Class<?>, Class<?>> interfaceImplementationMapping) { + this.interfaceImplementationMapping = interfaceImplementationMapping; + return this; + } + public MapperBuilder setFailOnUnknownProperties(final boolean failOnUnknownProperties) { this.failOnUnknownProperties = failOnUnknownProperties; return this; http://git-wip-us.apache.org/repos/asf/johnzon/blob/2507062b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java ---------------------------------------------------------------------- diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java index 44cea05..fd7d9db 100644 --- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java +++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java @@ -67,6 +67,7 @@ public /* DON'T MAKE IT HIDDEN */ class MapperConfig implements Cloneable { private final SerializeValueFilter serializeValueFilter; private final boolean useBigDecimalForFloats; private final Boolean deduplicateObjects; + private final Map<Class<?>, Class<?>> interfaceImplementationMapping; private final Map<Class<?>, ObjectConverter.Writer<?>> objectConverterWriterCache; private final Map<Class<?>, ObjectConverter.Reader<?>> objectConverterReaderCache; @@ -85,7 +86,8 @@ public /* DON'T MAKE IT HIDDEN */ class MapperConfig implements Cloneable { final boolean enforceQuoteString, final boolean failOnUnknown, final SerializeValueFilter serializeValueFilter, final boolean useBigDecimalForFloats, - final Boolean deduplicateObjects) { + final Boolean deduplicateObjects, + final Map<Class<?>, Class<?>> interfaceImplementationMapping) { //CHECKSTYLE:ON this.objectConverterWriters = objectConverterWriters; this.objectConverterReaders = objectConverterReaders; @@ -103,6 +105,7 @@ public /* DON'T MAKE IT HIDDEN */ class MapperConfig implements Cloneable { this.enforceQuoteString = enforceQuoteString; this.failOnUnknown = failOnUnknown; this.serializeValueFilter = serializeValueFilter == null ? (name, value) -> false : serializeValueFilter; + this.interfaceImplementationMapping = interfaceImplementationMapping; this.objectConverterWriterCache = new HashMap<Class<?>, ObjectConverter.Writer<?>>(objectConverterWriters.size()); this.objectConverterReaderCache = new HashMap<Class<?>, ObjectConverter.Reader<?>>(objectConverterReaders.size()); @@ -110,6 +113,9 @@ public /* DON'T MAKE IT HIDDEN */ class MapperConfig implements Cloneable { this.deduplicateObjects = deduplicateObjects; } + public Map<Class<?>, Class<?>> getInterfaceImplementationMapping() { + return interfaceImplementationMapping; + } public SerializeValueFilter getSerializeValueFilter() { return serializeValueFilter; http://git-wip-us.apache.org/repos/asf/johnzon/blob/2507062b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java ---------------------------------------------------------------------- diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java index b3dea58..d18d085 100644 --- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java +++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java @@ -339,11 +339,22 @@ public class Mappings { public ClassMapping findOrCreateClassMapping(final Type clazz) { ClassMapping classMapping = classes.get(clazz); if (classMapping == null) { - if (!Class.class.isInstance(clazz) || Map.class.isAssignableFrom(Class.class.cast(clazz))) { + if (!Class.class.isInstance(clazz)) { return null; } - - classMapping = createClassMapping(Class.class.cast(clazz)); + final Class asClass = Class.class.cast(clazz); + if (Map.class.isAssignableFrom(asClass) || asClass.isInterface()) { + final Class<?> mapping = config.getInterfaceImplementationMapping().get(clazz); + if (mapping != null) { + classMapping = createClassMapping(mapping); + } else if (asClass.getName().startsWith("java.")) { // we'll not be able to map it with pojo rules + return null; + } else { // assume that it can be written with pojo rules but not deserialized + classMapping = createClassMapping(asClass); + } + } else { + classMapping = createClassMapping(asClass); + } final ClassMapping existing = classes.putIfAbsent(clazz, classMapping); if (existing != null) { classMapping = existing; http://git-wip-us.apache.org/repos/asf/johnzon/blob/2507062b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/MapperConfigTest.java ---------------------------------------------------------------------- diff --git a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/MapperConfigTest.java b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/MapperConfigTest.java index 0a9cc7f..f1348fb 100644 --- a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/MapperConfigTest.java +++ b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/MapperConfigTest.java @@ -18,6 +18,8 @@ */ package org.apache.johnzon.mapper; +import static java.util.Collections.emptyMap; + import org.apache.johnzon.mapper.access.FieldAccessMode; import org.apache.johnzon.mapper.internal.AdapterKey; import org.junit.Assert; @@ -166,7 +168,7 @@ public class MapperConfigTest { new FieldAccessMode(true, true), Charset.forName("UTF-8"), null, - false, false, null, false, false); + false, false, null, false, false, emptyMap()); } http://git-wip-us.apache.org/repos/asf/johnzon/blob/2507062b/johnzon-mapper/src/test/java/org/superbiz/ExtendMappingTest.java ---------------------------------------------------------------------- diff --git a/johnzon-mapper/src/test/java/org/superbiz/ExtendMappingTest.java b/johnzon-mapper/src/test/java/org/superbiz/ExtendMappingTest.java index 142ca18..5e55ddf 100644 --- a/johnzon-mapper/src/test/java/org/superbiz/ExtendMappingTest.java +++ b/johnzon-mapper/src/test/java/org/superbiz/ExtendMappingTest.java @@ -28,11 +28,11 @@ import org.junit.Test; import java.lang.reflect.Type; import java.nio.charset.Charset; -import java.util.Comparator; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import static java.util.Collections.emptyMap; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -65,13 +65,8 @@ public class ExtendMappingTest { new HashMap<Class<?>, ObjectConverter.Reader<?>>(), -1, true, true, true, false, false, false, new FieldAccessMode(false, false), - Charset.forName("UTF-8"), - new Comparator<String>() { - @Override - public int compare(final String o1, final String o2) { - return o1.compareTo(o2); - } - }, false, false, null, false, false)); + Charset.forName("UTF-8"), String::compareTo, false, false, null, false, false, + emptyMap())); } @Override http://git-wip-us.apache.org/repos/asf/johnzon/blob/2507062b/src/site/markdown/index.md ---------------------------------------------------------------------- diff --git a/src/site/markdown/index.md b/src/site/markdown/index.md index eafd6e9..7ee99a3 100644 --- a/src/site/markdown/index.md +++ b/src/site/markdown/index.md @@ -296,7 +296,11 @@ Johnzon provides a module johnzon-jsonb implementing JSON-B standard based on Jo It fully reuses the JSON-B as API. -### Websocket (beta) +However it supports some specific properties to wire to the native johnzon configuration - see `JohnzonBuilder` for details. +One example is `johnzon.interfaceImplementationMapping` which will support a `Map<Class,Class>` to map interfaces to implementations +to use for deserialization. + +### Websocket <pre class="prettyprint linenums"><