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
The following commit(s) were added to refs/heads/master by this push: new 2e0f083 JOHNZON-220 adding from/to JsonValue/Pojo mapping method over Jsonb implementation - experimental from the spec, see https://github.com/eclipse-ee4j/jsonb-api/issues/111 2e0f083 is described below commit 2e0f083f25ed30b2bb20939f1cbcdadea46b6199 Author: Romain Manni-Bucau <rmannibu...@gmail.com> AuthorDate: Mon Jul 1 14:39:09 2019 +0200 JOHNZON-220 adding from/to JsonValue/Pojo mapping method over Jsonb implementation - experimental from the spec, see https://github.com/eclipse-ee4j/jsonb-api/issues/111 --- .../org/apache/johnzon/jsonb/JohnzonJsonb.java | 85 ++++++++++++- .../jsonb/api/experimental/JsonbExtension.java | 34 +++++ .../jsonb/api/experimental/JsonbExtensionTest.java | 97 ++++++++++++++ .../org/apache/johnzon/jsonb/test/JsonbRule.java | 139 +++++++++++++++++++++ .../java/org/apache/johnzon/mapper/Mapper.java | 15 ++- src/site/markdown/index.md | 3 + 6 files changed, 370 insertions(+), 3 deletions(-) diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonJsonb.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonJsonb.java index 644a5f1..adb5d66 100644 --- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonJsonb.java +++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonJsonb.java @@ -18,12 +18,18 @@ */ package org.apache.johnzon.jsonb; +import org.apache.johnzon.jsonb.api.experimental.JsonbExtension; import org.apache.johnzon.jsonb.extension.JsonValueReader; import org.apache.johnzon.jsonb.extension.JsonValueWriter; +import org.apache.johnzon.mapper.JsonObjectGenerator; import org.apache.johnzon.mapper.Mapper; import org.apache.johnzon.mapper.MapperException; import org.apache.johnzon.mapper.reflection.JohnzonParameterizedType; +import javax.json.JsonNumber; +import javax.json.JsonString; +import javax.json.JsonStructure; +import javax.json.JsonValue; import javax.json.bind.Jsonb; import javax.json.bind.JsonbException; import java.io.InputStream; @@ -34,6 +40,7 @@ import java.io.Writer; import java.lang.reflect.Array; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.math.BigInteger; import java.util.Collection; import java.util.Optional; import java.util.OptionalDouble; @@ -41,7 +48,7 @@ import java.util.OptionalInt; import java.util.OptionalLong; // TODO: Optional handling for lists (and arrays)? -public class JohnzonJsonb implements Jsonb, AutoCloseable { +public class JohnzonJsonb implements Jsonb, AutoCloseable, JsonbExtension { private final Mapper delegate; public JohnzonJsonb(final Mapper build) { @@ -385,4 +392,80 @@ public class JohnzonJsonb implements Jsonb, AutoCloseable { public void close() { delegate.close(); } + + @Override + public <T> T fromJsonValue(final JsonValue json, final Class<T> type) { + return fromJsonValue(json, Type.class.cast(type)); + } + + @Override + public JsonValue toJsonValue(final Object object) { + return toJsonValue(object, object.getClass()); + } + + @Override + public <T> T fromJsonValue(final JsonValue json, final Type type) { + switch (json.getValueType()) { + case NULL: + if (Class.class.isInstance(type) && Class.class.cast(type).isPrimitive()) { + throw new JsonbException("can't map a primritive to null"); + } + return null; + case STRING: + if (String.class != type) { + throw new JsonbException("STRING json can't be casted to " + type); + } + return (T) JsonString.class.cast(json).getString(); + case TRUE: + case FALSE: + if (Boolean.class != type && boolean.class != type) { + throw new JsonbException("TRUE and FALSE json can't be casted to " + type); + } + return (T) Boolean.valueOf(JsonValue.TRUE.equals(json)); + case NUMBER: + if (!Class.class.isInstance(type) || !Number.class.isAssignableFrom(Class.class.cast(type))) { + throw new JsonbException("NUMBER json can't be casted to " + type); + } + final JsonNumber jsonNumber = JsonNumber.class.cast(json); + if (int.class == type || Integer.class == type) { + return (T) Integer.valueOf(jsonNumber.intValue()); + } + if (long.class == type || Long.class == type) { + return (T) Long.valueOf(jsonNumber.longValue()); + } + if (double.class == type || Double.class == type) { + return (T) Double.valueOf(jsonNumber.doubleValue()); + } + if (float.class == type || Float.class == type) { + return (T) Float.valueOf((float) jsonNumber.doubleValue()); + } + if (byte.class == type || Byte.class == type) { + return (T) Byte.valueOf((byte) jsonNumber.intValue()); + } + if (short.class == type || Short.class == type) { + return (T) Short.valueOf((short) jsonNumber.intValue()); + } + if (BigInteger.class == type) { + return (T) jsonNumber.bigIntegerValue(); + } + return (T) jsonNumber.bigDecimalValue(); + case OBJECT: + case ARRAY: + return delegate.readObject(JsonStructure.class.cast(json), type); + default: + throw new JsonbException("Unsupported type: " + json.getValueType()); + } + } + + @Override + public JsonValue toJsonValue(final Object rawObject, final Type runtimeType) { + if (JsonValue.class.isInstance(rawObject)) { + return JsonValue.class.cast(rawObject); + } + try (final JsonObjectGenerator jsonObjectGenerator = new JsonObjectGenerator(delegate.getBuilderFactory())) { + delegate.writeObjectWithGenerator(unwrapOptional(rawObject), jsonObjectGenerator); + jsonObjectGenerator.flush(); + return jsonObjectGenerator.getResult(); + } + } } diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/api/experimental/JsonbExtension.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/api/experimental/JsonbExtension.java new file mode 100644 index 0000000..aa8c342 --- /dev/null +++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/api/experimental/JsonbExtension.java @@ -0,0 +1,34 @@ +/* + * 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.api.experimental; + +import java.lang.reflect.Type; + +import javax.json.JsonValue; + +// https://github.com/eclipse-ee4j/jsonb-api/issues/111 +public interface JsonbExtension { + <T> T fromJsonValue(JsonValue json, Class<T> type); + + <T> T fromJsonValue(JsonValue json, Type runtimeType); + + JsonValue toJsonValue(Object object); + + JsonValue toJsonValue (Object object, Type runtimeType); +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/api/experimental/JsonbExtensionTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/api/experimental/JsonbExtensionTest.java new file mode 100644 index 0000000..6ad6d66 --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/api/experimental/JsonbExtensionTest.java @@ -0,0 +1,97 @@ +/* + * 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.api.experimental; + +import static org.junit.Assert.assertEquals; + +import java.util.Collection; +import java.util.List; + +import javax.json.Json; +import javax.json.JsonObject; +import javax.json.JsonValue; + +import org.apache.johnzon.jsonb.test.JsonbRule; +import org.apache.johnzon.mapper.reflection.JohnzonParameterizedType; +import org.junit.Rule; +import org.junit.Test; + +public class JsonbExtensionTest { + @Rule + public final JsonbRule jsonb = new JsonbRule(); + + private final JsonObject defaultValue = Json.createObjectBuilder() + .add("foo", "ok") + .add("bar", 1) + .build(); + + @Test + public void fromJsonValue() { + final Value value = jsonb.fromJsonValue(defaultValue, Value.class); + assertEquals("ok", value.foo); + assertEquals(1, value.bar); + } + + @Test + public void fromJsonValue2() { + final JsonValue json = Json.createArrayBuilder() + .add(defaultValue) + .add(Json.createObjectBuilder() + .add("foo", "still ok") + .add("bar", 2) + .build()) + .build(); + final List<Value> values = jsonb.fromJsonValue(json, new JohnzonParameterizedType(Collection.class, Value.class)); + assertEquals(2, values.size()); + { + final Value value = values.get(0); + assertEquals("ok", value.foo); + assertEquals(1, value.bar); + } + { + final Value value = values.get(1); + assertEquals("still ok", value.foo); + assertEquals(2, value.bar); + } + } + + @Test + public void toJsonValue() { + assertEquals(defaultValue, jsonb.toJsonValue(new Value("ok", 1))); + } + + @Test + public void toJsonValue2() { + assertEquals(defaultValue, jsonb.toJsonValue(new Value("ok", 1), Value.class)); + } + + public static class Value { + public String foo; + public int bar; + + public Value() { + // no-op + } + + public Value(final String foo, final int bar) { + this.foo = foo; + this.bar = bar; + } + } +} diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/test/JsonbRule.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/test/JsonbRule.java new file mode 100644 index 0000000..024d3ca --- /dev/null +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/test/JsonbRule.java @@ -0,0 +1,139 @@ +/* + * 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.test; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.lang.reflect.Type; + +import javax.json.JsonValue; +import javax.json.bind.Jsonb; +import javax.json.bind.JsonbBuilder; +import javax.json.bind.JsonbException; + +import org.apache.johnzon.jsonb.api.experimental.JsonbExtension; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +public class JsonbRule implements TestRule, Jsonb, JsonbExtension { + private Jsonb jsonb; + + @Override + public Statement apply(Statement statement, Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + try (final Jsonb jsonb = JsonbBuilder.create()) { + JsonbRule.this.jsonb = jsonb; + statement.evaluate(); + } finally { + JsonbRule.this.jsonb = null; + } + } + }; + } + + @Override + public <T> T fromJson(final String str, final Class<T> type) throws JsonbException { + return jsonb.fromJson(str, type); + } + + @Override + public <T> T fromJson(final String str, final Type runtimeType) throws JsonbException { + return jsonb.fromJson(str, runtimeType); + } + + @Override + public <T> T fromJson(final Reader reader, final Class<T> type) throws JsonbException { + return jsonb.fromJson(reader, type); + } + + @Override + public <T> T fromJson(final Reader reader, final Type runtimeType) throws JsonbException { + return jsonb.fromJson(reader, runtimeType); + } + + @Override + public <T> T fromJson(final InputStream stream, final Class<T> type) throws JsonbException { + return jsonb.fromJson(stream, type); + } + + @Override + public <T> T fromJson(final InputStream stream, final Type runtimeType) throws JsonbException { + return jsonb.fromJson(stream, runtimeType); + } + + @Override + public String toJson(final Object object) throws JsonbException { + return jsonb.toJson(object); + } + + @Override + public String toJson(final Object object, final Type runtimeType) throws JsonbException { + return jsonb.toJson(object, runtimeType); + } + + @Override + public void toJson(final Object object, final Writer writer) throws JsonbException { + jsonb.toJson(object, writer); + } + + @Override + public void toJson(final Object object, final Type runtimeType, final Writer writer) throws JsonbException { + jsonb.toJson(object, runtimeType, writer); + } + + @Override + public void toJson(final Object object, final OutputStream stream) throws JsonbException { + jsonb.toJson(object, stream); + } + + @Override + public void toJson(final Object object, final Type runtimeType, final OutputStream stream) throws JsonbException { + jsonb.toJson(object, runtimeType, stream); + } + + @Override + public void close() { + // no-op + } + + @Override + public <T> T fromJsonValue(final JsonValue json, final Class<T> type) { + return JsonbExtension.class.cast(jsonb).fromJsonValue(json, type); + } + + @Override + public <T> T fromJsonValue(final JsonValue json, final Type runtimeType) { + return JsonbExtension.class.cast(jsonb).fromJsonValue(json, runtimeType); + } + + @Override + public JsonValue toJsonValue(final Object object) { + return JsonbExtension.class.cast(jsonb).toJsonValue(object); + } + + @Override + public JsonValue toJsonValue(final Object object, final Type runtimeType) { + return JsonbExtension.class.cast(jsonb).toJsonValue(object, runtimeType); + } +} diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java index a9ca674..459fa68 100644 --- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java +++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java @@ -198,11 +198,15 @@ public class Mapper implements Closeable { } try (final JsonGenerator generator = generatorFactory.createGenerator(stream(stream))) { - writeObject(object, generator, null, - isDeduplicateObjects(object.getClass()) ? new JsonPointerTracker(null, "/") : null); + writeObjectWithGenerator(object, generator); } } + public void writeObjectWithGenerator(final Object object, final JsonGenerator generator) { + writeObject(object, generator, null, + isDeduplicateObjects(object.getClass()) ? new JsonPointerTracker(null, "/") : null); + } + private boolean isDeduplicateObjects(Class<?> rootType) { Boolean dedup = config.isDeduplicateObjects(); if (dedup == null) { @@ -385,4 +389,11 @@ public class Mapper implements Closeable { } } + public JsonBuilderFactory getBuilderFactory() { + return builderFactory; + } + + public JsonProvider getProvider() { + return provider; + } } diff --git a/src/site/markdown/index.md b/src/site/markdown/index.md index 58058c3..7e97dc1 100644 --- a/src/site/markdown/index.md +++ b/src/site/markdown/index.md @@ -325,6 +325,9 @@ final JsonObject jsonObject = writer.getObject(); These two example will not use any IO and directly map the `JsonValue` to/from a POJO. +Also note that, as an experimental extension and pre-available feature of the next specification version, `org.apache.johnzon.jsonb.api.experimental.JsonbExtension` enables +to map POJO to `JsonValue` and the opposite. + ### Websocket <pre class="prettyprint linenums"><![CDATA[