JOHNZON-84 getting rid of default converters for primitives
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 7bdb597..dd8b79c 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
@@ -203,4 +203,8 @@
public void setEnforceQuoteString(final boolean val) {
builder.setEnforceQuoteString(val);
}
+
+ public void setPrimitiveConverters(final boolean val) {
+ builder.setPrimitiveConverters(val);
+ }
}
diff --git a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/WildcardConfigurableJohnzonProvider.java b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/WildcardConfigurableJohnzonProvider.java
index aa44fec..4df4a7c 100644
--- a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/WildcardConfigurableJohnzonProvider.java
+++ b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/WildcardConfigurableJohnzonProvider.java
@@ -211,4 +211,8 @@
public void setEnforceQuoteString(final boolean val) {
builder.setEnforceQuoteString(val);
}
+
+ public void setPrimitiveConverters(final boolean val) {
+ builder.setPrimitiveConverters(val);
+ }
}
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 ea2a937..da23f6a 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
@@ -192,6 +192,9 @@
config.getProperty("johnzon.enforceQuoteString")
.map(v -> !Boolean.class.isInstance(v) ? Boolean.parseBoolean(v.toString()) : Boolean.class.cast(v))
.ifPresent(builder::setEnforceQuoteString);
+ config.getProperty("johnzon.primitiveConverters")
+ .map(v -> !Boolean.class.isInstance(v) ? Boolean.parseBoolean(v.toString()) : Boolean.class.cast(v))
+ .ifPresent(builder::setPrimitiveConverters);
final Map<AdapterKey, Adapter<?, ?>> defaultConverters = createJava8Converters(builder);
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 9091698..944ed9b 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
@@ -80,6 +80,7 @@
DEFAULT_CONVERTERS.put(new AdapterKey(String.class, String.class), new ConverterAdapter<String>(new StringConverter()));
DEFAULT_CONVERTERS.put(new AdapterKey(BigDecimal.class, String.class), new ConverterAdapter<BigDecimal>(new BigDecimalConverter()));
DEFAULT_CONVERTERS.put(new AdapterKey(BigInteger.class, String.class), new ConverterAdapter<BigInteger>(new BigIntegerConverter()));
+ /* primitives should be hanlded low level and adapters will wrap them in string which is unlikely
DEFAULT_CONVERTERS.put(new AdapterKey(Byte.class, String.class), new ConverterAdapter<Byte>(new CachedDelegateConverter<Byte>(new ByteConverter())));
DEFAULT_CONVERTERS.put(new AdapterKey(Character.class, String.class), new ConverterAdapter<Character>(new CharacterConverter()));
DEFAULT_CONVERTERS.put(new AdapterKey(Double.class, String.class), new ConverterAdapter<Double>(new DoubleConverter()));
@@ -96,6 +97,7 @@
DEFAULT_CONVERTERS.put(new AdapterKey(long.class, String.class), DEFAULT_CONVERTERS.get(new AdapterKey(Long.class, String.class)));
DEFAULT_CONVERTERS.put(new AdapterKey(short.class, String.class), DEFAULT_CONVERTERS.get(new AdapterKey(Short.class, String.class)));
DEFAULT_CONVERTERS.put(new AdapterKey(boolean.class, String.class), DEFAULT_CONVERTERS.get(new AdapterKey(Boolean.class, String.class)));
+ */
DEFAULT_CONVERTERS.put(new AdapterKey(Locale.class, String.class), new LocaleConverter());
}
@@ -126,6 +128,7 @@
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 boolean primitiveConverters;
public Mapper build() {
if (readerFactory == null || generatorFactory == null) {
@@ -186,6 +189,25 @@
}
}
+ if (primitiveConverters) {
+ adapters.put(new AdapterKey(Byte.class, String.class), new ConverterAdapter<Byte>(new CachedDelegateConverter<Byte>(new ByteConverter())));
+ adapters.put(new AdapterKey(Character.class, String.class), new ConverterAdapter<Character>(new CharacterConverter()));
+ adapters.put(new AdapterKey(Double.class, String.class), new ConverterAdapter<Double>(new DoubleConverter()));
+ adapters.put(new AdapterKey(Float.class, String.class), new ConverterAdapter<Float>(new FloatConverter()));
+ adapters.put(new AdapterKey(Integer.class, String.class), new ConverterAdapter<Integer>(new IntegerConverter()));
+ adapters.put(new AdapterKey(Long.class, String.class), new ConverterAdapter<Long>(new LongConverter()));
+ adapters.put(new AdapterKey(Short.class, String.class), new ConverterAdapter<Short>(new ShortConverter()));
+ adapters.put(new AdapterKey(Boolean.class, String.class), new ConverterAdapter<Boolean>(new CachedDelegateConverter<Boolean>(new BooleanConverter())));
+ adapters.put(new AdapterKey(byte.class, String.class), adapters.get(new AdapterKey(Byte.class, String.class)));
+ adapters.put(new AdapterKey(char.class, String.class), adapters.get(new AdapterKey(Character.class, String.class)));
+ adapters.put(new AdapterKey(double.class, String.class), adapters.get(new AdapterKey(Double.class, String.class)));
+ adapters.put(new AdapterKey(float.class, String.class), adapters.get(new AdapterKey(Float.class, String.class)));
+ adapters.put(new AdapterKey(int.class, String.class), adapters.get(new AdapterKey(Integer.class, String.class)));
+ adapters.put(new AdapterKey(long.class, String.class), adapters.get(new AdapterKey(Long.class, String.class)));
+ adapters.put(new AdapterKey(short.class, String.class), adapters.get(new AdapterKey(Short.class, String.class)));
+ adapters.put(new AdapterKey(boolean.class, String.class), adapters.get(new AdapterKey(Boolean.class, String.class)));
+ }
+
return new Mapper(
readerFactory, generatorFactory,
new MapperConfig(
@@ -362,4 +384,9 @@
this.enforceQuoteString = val;
return this;
}
+
+ public MapperBuilder setPrimitiveConverters(final boolean val) {
+ this.primitiveConverters = val;
+ return this;
+ }
}
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGeneratorImpl.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGeneratorImpl.java
index 3ed0655..a88ab83 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGeneratorImpl.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGeneratorImpl.java
@@ -339,6 +339,9 @@
return;
}
}
+ if (writePrimitives(key, type, value)) {
+ return;
+ }
generator.writeStartObject(key);
doWriteObjectBody(value);
generator.writeEnd();
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java
index 7a9fcb2..b5a7fab 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java
@@ -21,6 +21,7 @@
import org.apache.johnzon.core.JsonLongImpl;
import org.apache.johnzon.core.JsonReaderImpl;
import org.apache.johnzon.mapper.access.AccessMode;
+import org.apache.johnzon.mapper.converter.CharacterConverter;
import org.apache.johnzon.mapper.converter.EnumConverter;
import org.apache.johnzon.mapper.internal.AdapterKey;
import org.apache.johnzon.mapper.internal.ConverterAdapter;
@@ -35,6 +36,9 @@
import javax.json.JsonValue;
import javax.xml.bind.DatatypeConverter;
import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
@@ -73,9 +77,10 @@
private static final Adapter<Object, String> FALLBACK_CONVERTER = new ConverterAdapter<Object>(new FallbackConverter());
private static final JohnzonParameterizedType ANY_LIST = new JohnzonParameterizedType(List.class, Object.class);
-
+ private static final CharacterConverter CHARACTER_CONVERTER = new CharacterConverter(); // this one is particular, share the logic
protected final ConcurrentMap<Adapter<?, ?>, AdapterKey> reverseAdaptersRegistry;
+ protected final ConcurrentMap<Class<?>, Method> valueOfs = new ConcurrentHashMap<Class<?>, Method>();
private final MapperConfig config;
private final Mappings mappings;
@@ -572,6 +577,38 @@
return text;
}
final Adapter converter = findAdapter(aClass);
+ Method method = valueOfs.get(aClass);
+ if (method == null && Class.class.isInstance(aClass)) { // handle primitives
+ final Class cast = Class.class.cast(aClass);
+ try {
+ method = cast.getMethod("valueOf", String.class);
+ if (Modifier.isPublic(method.getModifiers()) && Modifier.isStatic(method.getModifiers())) {
+ valueOfs.putIfAbsent(cast, method);
+ } else {
+ method = null;
+ }
+ } catch (final NoSuchMethodException e) {
+ // if a real primitive (very unlikely) try the wrapper
+ if (char.class == aClass) {
+ return CHARACTER_CONVERTER.fromString(text);
+ }
+ try {
+ return convertTo(Class.class.cast(cast.getField("TYPE").get(null)), text);
+ } catch (final Exception e1) {
+ // no-op
+ }
+ // no-op
+ }
+ }
+ if (method != null) {
+ try {
+ return method.invoke(null, text);
+ } catch (final IllegalAccessException e) {
+ throw new IllegalStateException(e);
+ } catch (final InvocationTargetException e) {
+ throw new MapperException(e.getCause());
+ }
+ }
if (converter == null) {
config.getAdapters().putIfAbsent(new AdapterKey(String.class, aClass), FALLBACK_CONVERTER);
return FALLBACK_CONVERTER.to(text);
diff --git a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/MapperTest.java b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/MapperTest.java
index 7967060..88466e3 100644
--- a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/MapperTest.java
+++ b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/MapperTest.java
@@ -191,7 +191,8 @@
put("a", "val");
put("b", true);
put("c", 1);
- }}, simpleMapper.readObject(new ByteArrayInputStream("{\"a\":\"val\", \"b\": true, \"c\": 1}".getBytes()), Object.class));
+ put("d", true);
+ }}, simpleMapper.readObject(new ByteArrayInputStream("{\"a\":\"val\", \"b\": true, \"c\": 1, \"d\": true}".getBytes()), Object.class));
// write
assertEquals("true", simpleMapper.writeObjectAsString(true));
@@ -199,12 +200,20 @@
assertEquals("1", simpleMapper.writeObjectAsString(1));
assertEquals("\"val\"", enforcedQuotes.writeObjectAsString("val"));
assertEquals("[\"val1\",\"val2\"]", simpleMapper.writeObjectAsString(asList("val1", "val2")));
- assertEquals("{\"a\":\"val\",\"b\":true,\"c\":1}", simpleMapper.writeObjectAsString(new TreeMap<String, Object>() {{
+ assertEquals("{\"a\":\"val\",\"b\":true,\"c\":1,\"d\":true}", simpleMapper.writeObjectAsString(new TreeMap<String, Object>() {{
put("a", "val");
put("b", true);
put("c", 1);
+ put("d", true);
}}));
}
+ { // in model
+ PrimitiveObject p = new PrimitiveObject();
+ p.bool = true;
+ final Mapper fieldMapper = new MapperBuilder().setAccessModeName("field").build();
+ assertEquals("{\"bool\":true}", fieldMapper.writeObjectAsString(p));
+ assertEquals(Boolean.TRUE, PrimitiveObject.class.cast(fieldMapper.readObject(new StringReader("{\"bool\":true}"), PrimitiveObject.class)).bool);
+ }
}
private void assertOneDimension(final Map<String, Object> data, final int size) {
@@ -1060,4 +1069,7 @@
}
}
+ public static class PrimitiveObject {
+ public Object bool;
+ }
}