This is an automated email from the ASF dual-hosted git repository. rmannibucau pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/johnzon.git
commit 69ea3926c00d4e4cffa86ae0ebdcc2b8185bad31 Author: Xavier Dury <[email protected]> AuthorDate: Tue Apr 2 19:58:45 2019 +0200 Fixed deserialization of primitives Serializers, Deserializers and Adapters ParameterizedTypes can be retrieved on super-classes/interfaces --- .../main/java/org/apache/johnzon/core/Types.java | 157 +++++++++++++++++++++ .../java/org/apache/johnzon/core/TypesTest.java | 48 +++++++ .../org/apache/johnzon/jsonb/JohnzonBuilder.java | 21 +-- .../johnzon/jsonb/JsonValueParserAdapter.java | 146 +++++++++++++++++++ .../org/apache/johnzon/jsonb/JsonbAccessMode.java | 25 +--- .../johnzon/jsonb/JohnzonConverterInJsonbTest.java | 5 +- .../java/org/apache/johnzon/jsonb/MoreTests.java | 156 ++++++++++++++++++++ .../org/apache/johnzon/mapper/MapperConfig.java | 15 +- .../apache/johnzon/mapper/MappingParserImpl.java | 7 +- .../org/apache/johnzon/mapper/ObjectConverter.java | 5 +- .../apache/johnzon/mapper/MapperConfigTest.java | 7 +- .../mapper/ObjectConverterWithAnnotationTest.java | 8 +- .../org/apache/johnzon/mapper/ObjectTypeTest.java | 10 +- 13 files changed, 551 insertions(+), 59 deletions(-) diff --git a/johnzon-core/src/main/java/org/apache/johnzon/core/Types.java b/johnzon-core/src/main/java/org/apache/johnzon/core/Types.java new file mode 100644 index 0000000..ec1baf9 --- /dev/null +++ b/johnzon-core/src/main/java/org/apache/johnzon/core/Types.java @@ -0,0 +1,157 @@ +/* + * 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.core; + +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; +import java.util.Arrays; + +public class Types { + + public static class TypeVisitor<T> { + + public T visit(Class<?> type) { + throw new UnsupportedOperationException("Visiting Class not supported."); + } + + public T visit(GenericArrayType type) { + throw new UnsupportedOperationException("Visiting GenericArrayType not supported."); + } + + public T visit(ParameterizedType type) { + throw new UnsupportedOperationException("Visiting ParameterizedType not supported."); + } + + public T visit(TypeVariable<?> type) { + throw new UnsupportedOperationException("Visiting TypeVariable not supported."); + } + + public T visit(WildcardType type) { + throw new UnsupportedOperationException("Visiting WildcardType not supported."); + } + + public final T visit(Type type) { + if (type instanceof Class<?>) { + return visit((Class<?>) type); + } + if (type instanceof ParameterizedType) { + return visit((ParameterizedType) type); + } + if (type instanceof WildcardType) { + return visit((WildcardType) type); + } + if (type instanceof TypeVariable<?>) { + return visit((TypeVariable<?>) type); + } + if (type instanceof GenericArrayType) { + return visit((GenericArrayType) type); + } + throw new IllegalArgumentException(String.format("Unknown type: %s", type.getClass())); + } + } + + private static class ArgumentTypeResolver extends TypeVisitor<Type[]> { + + private final Class<?> superType; + + public ArgumentTypeResolver(Class<?> superType) { + this.superType = superType; + } + + @Override + public Type[] visit(Class<?> type) { + if (this.superType.equals(type)) { + // May return Class[] instead of Type[], so copy it as a Type[] to avoid + // problems in visit(ParameterizedType) + return Arrays.copyOf(type.getTypeParameters(), superType.getTypeParameters().length, Type[].class); + } + if (type.getSuperclass() != null && this.superType.isAssignableFrom(type.getSuperclass())) { + return visit(type.getGenericSuperclass()); + } + Class<?>[] interfaces = type.getInterfaces(); + Type[] genericInterfaces = type.getGenericInterfaces(); + for (int i = 0; i < interfaces.length; i++) { + if (this.superType.isAssignableFrom(interfaces[i])) { + return visit(genericInterfaces[i]); + } + } + throw new IllegalArgumentException(String.format("%s is not assignable from %s", type, this.superType)); + } + + @Override + public Type[] visit(ParameterizedType type) { + Class<?> rawType = (Class<?>) type.getRawType(); // always a Class + TypeVariable<?>[] typeVariables = rawType.getTypeParameters(); + Type[] types = visit(rawType); + for (int i = 0; i < types.length; i++) { + if (types[i] instanceof TypeVariable<?>) { + TypeVariable<?> typeVariable = (TypeVariable<?>) types[i]; + for (int j = 0; j < typeVariables.length; j++) { + if (typeVariables[j].getName().equals(typeVariable.getName())) { + types[i] = type.getActualTypeArguments()[j]; + } + } + } + } + return types; + } + } + + private static class ParameterizedTypeImpl implements ParameterizedType { + + private final Type rawType; + private final Type[] arguments; + + public ParameterizedTypeImpl(Type rawType, Type... arguments) { + this.rawType = rawType; + this.arguments = arguments; + } + + @Override + public Type getRawType() { + return rawType; + } + + @Override + public Type getOwnerType() { + return null; + } + + @Override + public Type[] getActualTypeArguments() { + return arguments; + } + + } + + public static Type[] resolveArgumentTypes(Type type, Class<?> superClass) { + return new ArgumentTypeResolver(superClass).visit(type); + } + + public static ParameterizedType findParameterizedType(Type type, Class<?> superClass) { + return new ParameterizedTypeImpl(superClass, resolveArgumentTypes(type, superClass)); + } + + private Types() { + // no-op + } +} diff --git a/johnzon-core/src/test/java/org/apache/johnzon/core/TypesTest.java b/johnzon-core/src/test/java/org/apache/johnzon/core/TypesTest.java new file mode 100644 index 0000000..8e1fc20 --- /dev/null +++ b/johnzon-core/src/test/java/org/apache/johnzon/core/TypesTest.java @@ -0,0 +1,48 @@ +/* + * 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.core; + +import java.io.Serializable; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Arrays; + +import org.junit.Assert; +import org.junit.Test; + +public class TypesTest { + + interface GenericInterface<X, Y> {} + + interface PartialInterface<X> extends Serializable, GenericInterface<Integer, X> {} + + static abstract class AbstractClass<Z, Y> implements PartialInterface<Y> {} + + static class ConcreteClass extends AbstractClass<String, Boolean> {} + + @Test + public void test() { + ParameterizedType parameterizedType = Types.findParameterizedType(ConcreteClass.class, GenericInterface.class); + + Assert.assertTrue(Arrays.deepEquals(parameterizedType.getActualTypeArguments(), new Type[] { + Integer.class, + Boolean.class + })); + } +} 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 d146d79..7189a5d 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 @@ -84,6 +84,7 @@ import javax.json.stream.JsonParserFactory; import org.apache.johnzon.core.AbstractJsonFactory; import org.apache.johnzon.core.JsonGeneratorFactoryImpl; import org.apache.johnzon.core.JsonParserFactoryImpl; +import org.apache.johnzon.core.Types; import org.apache.johnzon.jsonb.cdi.CDIs; import org.apache.johnzon.jsonb.converter.JohnzonJsonbAdapter; import org.apache.johnzon.jsonb.factory.SimpleJohnzonAdapterFactory; @@ -274,10 +275,7 @@ public class JohnzonBuilder implements JsonbBuilder { config.getProperty(JsonbConfig.SERIALIZERS).map(JsonbSerializer[].class::cast).ifPresent(serializers -> { Stream.of(serializers).forEach(s -> { - final ParameterizedType pt = findPT(s, JsonbSerializer.class); - if (pt == null) { - throw new IllegalArgumentException(s + " doesn't implement JsonbSerializer"); - } + final ParameterizedType pt = Types.findParameterizedType(s.getClass(), JsonbSerializer.class); final Type[] args = pt.getActualTypeArguments(); // TODO: support PT in ObjectConverter (list) if (args.length != 1 || !Class.class.isInstance(args[0])) { @@ -292,10 +290,7 @@ public class JohnzonBuilder implements JsonbBuilder { }); config.getProperty(JsonbConfig.DESERIALIZERS).map(JsonbDeserializer[].class::cast).ifPresent(deserializers -> { Stream.of(deserializers).forEach(d -> { - final ParameterizedType pt = findPT(d, JsonbDeserializer.class); - if (pt == null) { - throw new IllegalArgumentException(d + " doesn't implement JsonbDeserializer"); - } + final ParameterizedType pt = Types.findParameterizedType(d.getClass(), JsonbDeserializer.class); final Type[] args = pt.getActualTypeArguments(); if (args.length != 1 || !Class.class.isInstance(args[0])) { throw new IllegalArgumentException("We only support deserializer on Class for now"); @@ -304,7 +299,7 @@ public class JohnzonBuilder implements JsonbBuilder { builder.addObjectConverter( Class.class.cast(args[0]), (ObjectConverter.Reader) (jsonObject, targetType, parser) -> d.deserialize( - parserFactoryProvider.get().createParser(jsonObject), new JohnzonDeserializationContext(parser), targetType)); + JsonValueParserAdapter.createFor(jsonObject, parserFactoryProvider), new JohnzonDeserializationContext(parser), targetType)); }); }); @@ -331,6 +326,7 @@ public class JohnzonBuilder implements JsonbBuilder { } } : new JohnzonJsonb(mapper); } + private AccessMode toAccessMode(final Object s) { if (String.class.isInstance(s)) { @@ -368,13 +364,6 @@ public class JohnzonBuilder implements JsonbBuilder { }; } - private ParameterizedType findPT(final Object s, final Class<?> type) { - return ParameterizedType.class.cast( - Stream.of(s.getClass().getGenericInterfaces()) - .filter(i -> ParameterizedType.class.isInstance(i) && ParameterizedType.class.cast(i).getRawType() == type) - .findFirst().orElse(null)); - } - private Object getBeanManager() { if (beanManager == null) { try { // don't trigger CDI if not there diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonValueParserAdapter.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonValueParserAdapter.java new file mode 100644 index 0000000..d7b9991 --- /dev/null +++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonValueParserAdapter.java @@ -0,0 +1,146 @@ +/* + * 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 java.math.BigDecimal; +import java.util.EnumSet; +import java.util.function.Supplier; + +import javax.json.JsonArray; +import javax.json.JsonNumber; +import javax.json.JsonObject; +import javax.json.JsonString; +import javax.json.JsonValue; +import javax.json.JsonValue.ValueType; +import javax.json.stream.JsonLocation; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParserFactory; + +class JsonValueParserAdapter<T extends JsonValue> implements JsonParser { + + private static class JsonStringParserAdapter extends JsonValueParserAdapter<JsonString> { + + public JsonStringParserAdapter(JsonString jsonValue) { + super(jsonValue); + } + + @Override + public String getString() { + return getValue().getString(); + } + } + + private static class JsonNumberParserAdapter extends JsonValueParserAdapter<JsonNumber> { + + public JsonNumberParserAdapter(JsonNumber jsonValue) { + super(jsonValue); + } + + @Override + public boolean isIntegralNumber() { + return getValue().isIntegral(); + } + + @Override + public int getInt() { + return getValue().intValueExact(); + } + + @Override + public long getLong() { + return getValue().longValueExact(); + } + + @Override + public BigDecimal getBigDecimal() { + return getValue().bigDecimalValue(); + } + } + + public static JsonParser createFor(JsonValue jsonValue, Supplier<JsonParserFactory> parserFactoryProvider) { + if (jsonValue instanceof JsonObject) { + return parserFactoryProvider.get().createParser((JsonObject) jsonValue); + } else if (jsonValue instanceof JsonArray) { + return parserFactoryProvider.get().createParser((JsonArray) jsonValue); + } else if (jsonValue instanceof JsonString) { + return new JsonStringParserAdapter((JsonString) jsonValue); + } else if (jsonValue instanceof JsonNumber) { + return new JsonNumberParserAdapter((JsonNumber) jsonValue); + } else if (EnumSet.of(ValueType.FALSE, ValueType.TRUE).contains(jsonValue.getValueType())) { + return new JsonValueParserAdapter<>(jsonValue); + } + throw new IllegalArgumentException("Cannot create JsonParser for " + jsonValue.getValueType()); + } + + private final T jsonValue; + + JsonValueParserAdapter(T jsonValue) { + this.jsonValue = jsonValue; + } + + @Override + public boolean hasNext() { + return false; + } + + @Override + public Event next() { + throw new UnsupportedOperationException("next() no supported for " + jsonValue.getValueType()); + } + + @Override + public String getString() { + throw new UnsupportedOperationException("next() no supported for " + jsonValue.getValueType()); + } + + @Override + public boolean isIntegralNumber() { + throw new UnsupportedOperationException("isIntegralNumber() not supported for " + jsonValue.getValueType()); + } + + @Override + public int getInt() { + throw new UnsupportedOperationException("getInt() not supported for " + jsonValue.getValueType()); + } + + @Override + public long getLong() { + throw new UnsupportedOperationException("getLong() not supported for " + jsonValue.getValueType()); + } + + @Override + public BigDecimal getBigDecimal() { + throw new UnsupportedOperationException("getBigDecimal() not supported for " + jsonValue.getValueType()); + } + + @Override + public JsonLocation getLocation() { + throw new UnsupportedOperationException("getLocation() not supported for " + jsonValue.getValueType()); + } + + @Override + public void close() { + // no-op + } + + @Override + public T getValue() { + return jsonValue; + } +} diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java index e5718fb..c0fcd33 100644 --- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java +++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java @@ -18,6 +18,7 @@ */ package org.apache.johnzon.jsonb; +import org.apache.johnzon.core.Types; import org.apache.johnzon.jsonb.converter.JohnzonJsonbAdapter; import org.apache.johnzon.jsonb.converter.JsonbDateConverter; import org.apache.johnzon.jsonb.converter.JsonbLocalDateConverter; @@ -63,6 +64,7 @@ import javax.json.bind.config.PropertyVisibilityStrategy; import javax.json.bind.serializer.JsonbDeserializer; import javax.json.bind.serializer.JsonbSerializer; import javax.json.stream.JsonParserFactory; + import java.io.Closeable; import java.io.IOException; import java.lang.annotation.Annotation; @@ -335,10 +337,7 @@ public class JsonbAccessMode implements AccessMode, Closeable { final Adapter converter; if (adapter != null) { final Class<? extends JsonbAdapter> value = adapter.value(); - final ParameterizedType pt = findPt(value, JsonbAdapter.class); - if (pt == null) { - throw new IllegalArgumentException(value + " doesn't implement JsonbAdapter"); - } + final ParameterizedType pt = Types.findParameterizedType(value, JsonbAdapter.class); final JohnzonAdapterFactory.Instance<? extends JsonbAdapter> instance = newInstance(value); toRelease.add(instance); final Type[] actualTypeArguments = pt.getActualTypeArguments(); @@ -363,12 +362,6 @@ public class JsonbAccessMode implements AccessMode, Closeable { return converter; } - private ParameterizedType findPt(final Class<?> value, final Class<?> type) { - return ParameterizedType.class.cast( - Stream.of(value.getGenericInterfaces()) - .filter(i -> ParameterizedType.class.isInstance(i) && ParameterizedType.class.cast(i).getRawType() == type).findFirst().orElse(null)); - } - private JohnzonAdapterFactory.Instance newInstance(final Class<?> value) { return factory.create(value); } @@ -736,14 +729,11 @@ public class JsonbAccessMode implements AccessMode, Closeable { if (deserializer != null) { final Class<? extends JsonbDeserializer> value = deserializer.value(); - final ParameterizedType pt = findPt(value, JsonbDeserializer.class); - if (pt == null) { - throw new IllegalArgumentException(value + " doesn't implement JsonbDeserializer"); - } + final ParameterizedType pt = Types.findParameterizedType(value, JsonbDeserializer.class); final JohnzonAdapterFactory.Instance<? extends JsonbDeserializer> instance = newInstance(value); toRelease.add(instance); reader = (jsonObject, targetType, parser) -> - instance.getValue().deserialize(parserFactory.get().createParser(jsonObject), new JohnzonDeserializationContext(parser), targetType); + instance.getValue().deserialize(JsonValueParserAdapter.createFor(jsonObject, parserFactory), new JohnzonDeserializationContext(parser), targetType); } else if (johnzonConverter != null) { try { MapperConverter mapperConverter = johnzonConverter.value().newInstance(); @@ -781,10 +771,7 @@ public class JsonbAccessMode implements AccessMode, Closeable { if (serializer != null) { final Class<? extends JsonbSerializer> value = serializer.value(); - final ParameterizedType pt = findPt(value, JsonbSerializer.class); - if (pt == null) { - throw new IllegalArgumentException(value + " doesn't implement JsonbSerializer"); - } + final ParameterizedType pt = Types.findParameterizedType(value, JsonbSerializer.class); final JohnzonAdapterFactory.Instance<? extends JsonbSerializer> instance = newInstance(value); toRelease.add(instance); writer = (instance1, jsonbGenerator) -> diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JohnzonConverterInJsonbTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JohnzonConverterInJsonbTest.java index f9de06a..c72cac5 100644 --- a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JohnzonConverterInJsonbTest.java +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JohnzonConverterInJsonbTest.java @@ -24,6 +24,7 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import javax.json.JsonObject; +import javax.json.JsonValue; import javax.json.bind.Jsonb; import javax.json.bind.spi.JsonbProvider; @@ -92,9 +93,9 @@ public class JohnzonConverterInJsonbTest { } @Override - public TestDTO fromJson(JsonObject jsonObject, Type targetType, MappingParser parser) { + public TestDTO fromJson(JsonValue jsonValue, Type targetType, MappingParser parser) { TestDTO dto = new TestDTO(); - dto.instant = ZonedDateTime.parse(jsonObject.getString(TIMESTAMP_JSON_KEY)).toInstant(); + dto.instant = ZonedDateTime.parse(((JsonObject) jsonValue).getString(TIMESTAMP_JSON_KEY)).toInstant(); return dto; } } diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/MoreTests.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/MoreTests.java new file mode 100644 index 0000000..f205ff0 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/MoreTests.java @@ -0,0 +1,156 @@ +/* + * 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 java.io.StringWriter; +import java.lang.reflect.Type; +import java.util.UUID; + +import javax.json.JsonValue; +import javax.json.bind.Jsonb; +import javax.json.bind.JsonbBuilder; +import javax.json.bind.adapter.JsonbAdapter; +import javax.json.bind.annotation.JsonbTypeAdapter; +import javax.json.bind.annotation.JsonbTypeDeserializer; +import javax.json.bind.annotation.JsonbTypeSerializer; +import javax.json.bind.serializer.DeserializationContext; +import javax.json.bind.serializer.JsonbDeserializer; +import javax.json.bind.serializer.JsonbSerializer; +import javax.json.bind.serializer.SerializationContext; +import javax.json.stream.JsonGenerator; +import javax.json.stream.JsonParser; + +import org.junit.Test; + +public class MoreTests { + + // Does not seem to work with enums + public static class Option { + + public static final Option YES = new Option(true); + public static final Option NO = new Option(false); + + private final boolean value; + + private Option(boolean value) { + this.value = value; + } + + public boolean asBoolean() { + return value; + } + + public static Option of(boolean value) { + return value ? YES : NO; + } + } + + public static class VATNumber { + + private final long value; + + public VATNumber(long value) { + this.value = value; + } + + public long getValue() { + return value; + } + } + + public interface Composite<T, X> extends JsonbSerializer<T>, JsonbDeserializer<T>, JsonbAdapter<T, X> {} + + public static abstract class AbstractComposite<T, X> implements Composite<T, X> {} + + public static abstract class StringValueComposite<T> extends AbstractComposite<T, String> {} + + public static class UUIDComposite extends StringValueComposite<UUID> { + + public void serialize(UUID obj, JsonGenerator generator, SerializationContext ctx) { + generator.write(obj.toString()); + } + + public UUID deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { + return UUID.fromString(parser.getString()); + } + + @Override + public UUID adaptFromJson(String obj) throws Exception { + return UUID.fromString(obj); + } + + @Override + public String adaptToJson(UUID obj) throws Exception { + return obj.toString(); + } + } + + public static class OptionDeSer implements JsonbSerializer<Option>, JsonbDeserializer<Option> { + + @Override + public Option deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { + return Option.of(parser.getValue().equals(JsonValue.TRUE)); + } + + @Override + public void serialize(Option obj, JsonGenerator generator, SerializationContext ctx) { + generator.write(obj.asBoolean()); + } + } + + public static class VATDeSer implements JsonbSerializer<VATNumber>, JsonbDeserializer<VATNumber> { + + @Override + public VATNumber deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) { + return new VATNumber(parser.getLong()); + } + + @Override + public void serialize(VATNumber obj, JsonGenerator generator, SerializationContext ctx) { + generator.write(obj.getValue()); + } + } + + public static class Wrapper { + + @JsonbTypeSerializer(UUIDComposite.class) + @JsonbTypeDeserializer(UUIDComposite.class) + public UUID uuid = UUID.randomUUID(); + + @JsonbTypeAdapter(UUIDComposite.class) + public UUID uuid2 = UUID.randomUUID(); + + @JsonbTypeSerializer(OptionDeSer.class) + @JsonbTypeDeserializer(OptionDeSer.class) + public Option option = Option.YES; + + @JsonbTypeSerializer(VATDeSer.class) + @JsonbTypeDeserializer(VATDeSer.class) + public VATNumber vatNumber = new VATNumber(42); + + } + + @Test + public void testIt() { + Jsonb jsonb = JsonbBuilder.create(); + StringWriter w = new StringWriter(); + jsonb.toJson(new Wrapper(), w); + jsonb.fromJson(w.toString(), Wrapper.class); + } +} 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 fd7d9db..f469a2b 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 @@ -18,12 +18,6 @@ */ package org.apache.johnzon.mapper; -import org.apache.johnzon.mapper.access.AccessMode; -import org.apache.johnzon.mapper.converter.EnumConverter; -import org.apache.johnzon.mapper.internal.AdapterKey; -import org.apache.johnzon.mapper.internal.ConverterAdapter; - -import javax.json.JsonObject; import java.lang.reflect.Type; import java.nio.charset.Charset; import java.util.Comparator; @@ -31,6 +25,13 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentMap; +import javax.json.JsonValue; + +import org.apache.johnzon.mapper.access.AccessMode; +import org.apache.johnzon.mapper.converter.EnumConverter; +import org.apache.johnzon.mapper.internal.AdapterKey; +import org.apache.johnzon.mapper.internal.ConverterAdapter; + /** * Contains internal configuration for all the mapper stuff. * It needs to be immutable and 100% runtime oriented. @@ -44,7 +45,7 @@ public /* DON'T MAKE IT HIDDEN */ class MapperConfig implements Cloneable { } @Override - public Object fromJson(JsonObject jsonObject, Type targetType, MappingParser parser) { + public Object fromJson(JsonValue jsonObject, Type targetType, MappingParser parser) { return null; } }; 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 4ad1633..18b3298 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 @@ -33,6 +33,7 @@ import javax.json.JsonReader; import javax.json.JsonString; import javax.json.JsonStructure; import javax.json.JsonValue; +import javax.json.JsonValue.ValueType; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; @@ -727,9 +728,9 @@ public class MappingParserImpl implements MappingParser { final JsonPointerTracker jsonPointer, final Type rootType) { if (objectConverter != null) { - - if (jsonValue instanceof JsonObject) { - return objectConverter.fromJson((JsonObject) jsonValue, type, this); + + if (EnumSet.of(ValueType.OBJECT, ValueType.NUMBER, ValueType.STRING, ValueType.FALSE, ValueType.TRUE).contains(jsonValue.getValueType())) { + return objectConverter.fromJson(jsonValue, type, this); } else if (jsonValue instanceof JsonArray) { return buildArray(type, jsonValue.asJsonArray(), itemConverter, objectConverter, jsonPointer, rootType); } else { diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/ObjectConverter.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/ObjectConverter.java index 03ce47e..4a5db20 100644 --- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/ObjectConverter.java +++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/ObjectConverter.java @@ -18,9 +18,10 @@ */ package org.apache.johnzon.mapper; -import javax.json.JsonObject; import java.lang.reflect.Type; +import javax.json.JsonValue; + /** * Convert a given Java Type a nested JSON representation. * And the other way around. @@ -39,7 +40,7 @@ public final class ObjectConverter { } public interface Reader<T> extends MapperConverter { - T fromJson(JsonObject jsonObject, Type targetType, MappingParser parser); + T fromJson(JsonValue jsonValue, Type targetType, MappingParser parser); } public interface Codec<T> extends ObjectConverter.Writer<T>, ObjectConverter.Reader<T> { 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 f1348fb..c447606 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 @@ -25,7 +25,8 @@ import org.apache.johnzon.mapper.internal.AdapterKey; import org.junit.Assert; import org.junit.Test; -import javax.json.JsonObject; +import javax.json.JsonValue; + import java.lang.reflect.Type; import java.nio.charset.Charset; import java.util.Collections; @@ -191,7 +192,7 @@ public class MapperConfigTest { } @Override - public T fromJson(JsonObject jsonObject, Type targetType, MappingParser parser) { + public T fromJson(JsonValue jsonObject, Type targetType, MappingParser parser) { // dummy return null; } @@ -204,7 +205,7 @@ public class MapperConfigTest { } @Override - public T fromJson(JsonObject jsonObject, Type targetType, MappingParser parser) { + public T fromJson(JsonValue jsonObject, Type targetType, MappingParser parser) { // dummy return null; } diff --git a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectConverterWithAnnotationTest.java b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectConverterWithAnnotationTest.java index c465f8b..2c00f32 100644 --- a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectConverterWithAnnotationTest.java +++ b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectConverterWithAnnotationTest.java @@ -24,6 +24,8 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import javax.json.JsonObject; +import javax.json.JsonValue; + import java.beans.ConstructorProperties; import java.lang.reflect.Type; import java.util.Arrays; @@ -395,9 +397,9 @@ public class ObjectConverterWithAnnotationTest { } @Override - public Bike fromJson(JsonObject jsonObject, Type targetType, MappingParser parser) { - return new Bike(MANUFACTURERS.get(jsonObject.getInt(MANUFACTURER_ID)), - BikeType.values()[jsonObject.getInt(TYPE_INDEX)]); + public Bike fromJson(JsonValue jsonObject, Type targetType, MappingParser parser) { + return new Bike(MANUFACTURERS.get(((JsonObject) jsonObject).getInt(MANUFACTURER_ID)), + BikeType.values()[((JsonObject) jsonObject).getInt(TYPE_INDEX)]); } } } diff --git a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java index adfe0e5..e4a56e0 100644 --- a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java +++ b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java @@ -25,6 +25,8 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import javax.json.JsonObject; +import javax.json.JsonValue; + import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; @@ -205,8 +207,8 @@ public class ObjectTypeTest { } @Override - public Dog fromJson(JsonObject jsonObject, Type targetType, MappingParser parser) { - String javaType = jsonObject.getString("//javaType"); + public Dog fromJson(JsonValue jsonObject, Type targetType, MappingParser parser) { + String javaType = ((JsonObject) jsonObject).getString("//javaType"); Class targetClass = javaType != null ? getSubClass(targetType, javaType) : (Class) targetType; return parser.readObject(jsonObject, targetClass); @@ -424,8 +426,8 @@ public class ObjectTypeTest { } @Override - public Poodle fromJson(JsonObject jsonObject, Type targetType, MappingParser parser) { - return POODLES.get(jsonObject.getString("poodleName")); + public Poodle fromJson(JsonValue jsonObject, Type targetType, MappingParser parser) { + return POODLES.get(((JsonObject) jsonObject).getString("poodleName")); } }
