You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@johnzon.apache.org by rm...@apache.org on 2019/03/03 16:05:31 UTC

[johnzon] branch master updated: JOHNZON-203 first round of impl for JsonValue <-> pojo binding bypassing IO steps

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 f28196b  JOHNZON-203 first round of impl for JsonValue <-> pojo binding bypassing IO steps
f28196b is described below

commit f28196b39ae3d0ff2ccdd8b1e9efe2a5801e1e7f
Author: Romain Manni-Bucau <rm...@apache.org>
AuthorDate: Sun Mar 3 17:05:20 2019 +0100

    JOHNZON-203 first round of impl for JsonValue <-> pojo binding bypassing IO steps
---
 .../org/apache/johnzon/jsonb/JohnzonJsonb.java     |  20 ++
 .../johnzon/jsonb/extension/JsonValueReader.java   |  54 ++++
 .../johnzon/jsonb/extension/JsonValueWriter.java   |  60 +++++
 .../apache/johnzon/jsonb/JsonbJsonValueTest.java   |  57 ++++
 .../apache/johnzon/mapper/JsonObjectGenerator.java | 291 +++++++++++++++++++++
 .../java/org/apache/johnzon/mapper/Mapper.java     | 109 +++++++-
 .../org/apache/johnzon/mapper/MapperBuilder.java   |  27 +-
 .../java/org/apache/johnzon/mapper/MapperTest.java |  46 ++++
 .../java/org/superbiz/MultiStructureObject.java    |  34 +++
 src/site/markdown/index.md                         |  21 +-
 10 files changed, 702 insertions(+), 17 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 9894228..644a5f1 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,6 +18,8 @@
  */
 package org.apache.johnzon.jsonb;
 
+import org.apache.johnzon.jsonb.extension.JsonValueReader;
+import org.apache.johnzon.jsonb.extension.JsonValueWriter;
 import org.apache.johnzon.mapper.Mapper;
 import org.apache.johnzon.mapper.MapperException;
 import org.apache.johnzon.mapper.reflection.JohnzonParameterizedType;
@@ -124,6 +126,10 @@ public class JohnzonJsonb implements Jsonb, AutoCloseable {
 
     @Override
     public <T> T fromJson(final Reader reader, final Class<T> type) throws JsonbException {
+        if (JsonValueReader.class.isInstance(reader)) {
+            return delegate.readObject(JsonValueReader.class.cast(reader).getInput(), type);
+        }
+
         try {
             if (isArray(type)) {
                 return delegate.readTypedArray(reader, type.getComponentType(), type);
@@ -143,6 +149,10 @@ public class JohnzonJsonb implements Jsonb, AutoCloseable {
 
     @Override
     public <T> T fromJson(final Reader reader, final Type runtimeType) throws JsonbException {
+        if (JsonValueReader.class.isInstance(reader)) {
+            return delegate.readObject(JsonValueReader.class.cast(reader).getInput(), runtimeType);
+        }
+
         try {
             if (isArray(runtimeType)) {
                 final Class<T> type = Class.class.cast(runtimeType);
@@ -284,6 +294,11 @@ public class JohnzonJsonb implements Jsonb, AutoCloseable {
 
     @Override
     public void toJson(final Object inObject, final Writer writer) throws JsonbException {
+        if (JsonValueWriter.class.isInstance(writer)) {
+            JsonValueWriter.class.cast(writer).setResult(delegate.toStructure(inObject));
+            return;
+        }
+
         final Object object = unwrapOptional(inObject);
         if (object != null && isArray(object.getClass())) {
             delegate.writeArray((Object[]) object, writer);
@@ -296,6 +311,11 @@ public class JohnzonJsonb implements Jsonb, AutoCloseable {
 
     @Override
     public void toJson(final Object inObject, final Type runtimeType, final Writer writer) throws JsonbException {
+        if (JsonValueWriter.class.isInstance(writer)) {
+            JsonValueWriter.class.cast(writer).setResult(delegate.toStructure(inObject));
+            return;
+        }
+
         final Object object = unwrapOptional(inObject);
         if (object != null && isArray(runtimeType)) {
             delegate.writeArray((Object[]) object, writer);
diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/extension/JsonValueReader.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/extension/JsonValueReader.java
new file mode 100644
index 0000000..222a6e0
--- /dev/null
+++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/extension/JsonValueReader.java
@@ -0,0 +1,54 @@
+/*
+ * 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.extension;
+
+import java.io.Reader;
+
+import javax.json.JsonStructure;
+
+public class JsonValueReader<T> extends Reader {
+    private final JsonStructure input;
+    private T result;
+
+    public JsonValueReader(final JsonStructure input) {
+        this.input = input;
+    }
+
+    @Override
+    public int read(final char[] cbuf, final int off, final int len) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void close() {
+        // no-op
+    }
+
+    public JsonStructure getInput() {
+        return input;
+    }
+
+    public T getResult() {
+        return result;
+    }
+
+    public void setResult(final T result) {
+        this.result = result;
+    }
+}
diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/extension/JsonValueWriter.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/extension/JsonValueWriter.java
new file mode 100644
index 0000000..a861658
--- /dev/null
+++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/extension/JsonValueWriter.java
@@ -0,0 +1,60 @@
+/*
+ * 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.extension;
+
+import java.io.Writer;
+
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import javax.json.JsonValue;
+
+public class JsonValueWriter extends Writer {
+    private JsonValue result;
+
+    @Override
+    public void write(final char[] cbuf, final int off, final int len) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void flush() {
+        // no-op
+    }
+
+    @Override
+    public void close() {
+        flush();
+    }
+
+    public void setResult(final JsonValue result) {
+        this.result = result;
+    }
+
+    public JsonValue getResult() {
+        return result;
+    }
+
+    public JsonObject getObject() {
+        return result.asJsonObject();
+    }
+
+    public JsonArray getArray() {
+        return result.asJsonArray();
+    }
+}
diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonbJsonValueTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonbJsonValueTest.java
new file mode 100644
index 0000000..a21f28a
--- /dev/null
+++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonbJsonValueTest.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 org.junit.Assert.assertEquals;
+
+import javax.json.Json;
+import javax.json.JsonObject;
+import javax.json.bind.Jsonb;
+import javax.json.bind.JsonbBuilder;
+
+import org.apache.johnzon.jsonb.extension.JsonValueReader;
+import org.apache.johnzon.jsonb.extension.JsonValueWriter;
+import org.junit.Test;
+
+public class JsonbJsonValueTest {
+    @Test
+    public void from() throws Exception {
+        final JsonValueReader<Simple> reader = new JsonValueReader<>(Json.createObjectBuilder().add("value", "simple").build());
+        final Jsonb jsonb = JsonbBuilder.create();
+        final Simple simple = jsonb.fromJson(reader, Simple.class);
+        jsonb.close();
+        assertEquals("simple", simple.value);
+    }
+
+    @Test
+    public void to() throws Exception {
+        final Simple object = new Simple();
+        object.value = "simple";
+        final JsonValueWriter writer = new JsonValueWriter();
+        final Jsonb jsonb = JsonbBuilder.create();
+        jsonb.toJson(object, writer);
+        jsonb.close();
+        final JsonObject jsonObject = writer.getObject();
+        assertEquals("{\"value\":\"simple\"}", jsonObject.toString());
+    }
+
+    public static class Simple {
+        public String value;
+    }
+}
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JsonObjectGenerator.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JsonObjectGenerator.java
new file mode 100644
index 0000000..9f8b380
--- /dev/null
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JsonObjectGenerator.java
@@ -0,0 +1,291 @@
+/*
+ * 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.mapper;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.LinkedList;
+
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonBuilderFactory;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonValue;
+import javax.json.stream.JsonGenerator;
+
+// assume usage is right, since it is an internal based on the fact we correctly use jsongenerator api it is fine
+// todo: drop reflection, it is not needed here but it was simpler for a first impl
+public class JsonObjectGenerator implements JsonGenerator {
+    private final JsonBuilderFactory factory;
+    private final LinkedList<Object> builders = new LinkedList<>();
+
+    private JsonObjectBuilder objectBuilder;
+    private JsonArrayBuilder arrayBuilder;
+
+    public JsonObjectGenerator(final JsonBuilderFactory factory) {
+        this.factory = factory;
+    }
+
+    @Override
+    public JsonGenerator writeStartObject() {
+        objectBuilder = factory.createObjectBuilder();
+        builders.add(objectBuilder);
+        arrayBuilder = null;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartObject(final String name) {
+        objectBuilder = factory.createObjectBuilder();
+        builders.add(new NamedBuilder<>(objectBuilder, name));
+        arrayBuilder = null;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartArray() {
+        arrayBuilder = factory.createArrayBuilder();
+        builders.add(arrayBuilder);
+        objectBuilder = null;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartArray(final String name) {
+        arrayBuilder = factory.createArrayBuilder();
+        builders.add(new NamedBuilder<>(arrayBuilder, name));
+        objectBuilder = null;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeKey(final String name) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public JsonGenerator write(final String name, final JsonValue value) {
+        objectBuilder.add(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final String name, final String value) {
+        objectBuilder.add(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final String name, final BigInteger value) {
+        objectBuilder.add(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final String name, final BigDecimal value) {
+        objectBuilder.add(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final String name, final int value) {
+        objectBuilder.add(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final String name, final long value) {
+        objectBuilder.add(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final String name, final double value) {
+        objectBuilder.add(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final String name, final boolean value) {
+        objectBuilder.add(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeNull(final String name) {
+        objectBuilder.addNull(name);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final JsonValue value) {
+        arrayBuilder.add(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final String value) {
+        arrayBuilder.add(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final BigDecimal value) {
+        arrayBuilder.add(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final BigInteger value) {
+        arrayBuilder.add(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final int value) {
+        arrayBuilder.add(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final long value) {
+        arrayBuilder.add(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final double value) {
+        arrayBuilder.add(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final boolean value) {
+        arrayBuilder.add(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeNull() {
+        arrayBuilder.addNull();
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeEnd() {
+        if (builders.size() == 1) {
+            return this;
+        }
+
+        final Object last = builders.removeLast();
+
+        /*
+         * Previous potential cases:
+         * 1. json array -> we add the builder directly
+         * 2. NamedBuilder{array|object} -> we add the builder in the previous object
+         */
+
+        final String name;
+        Object previous = builders.getLast();
+        if (NamedBuilder.class.isInstance(previous)) {
+            final NamedBuilder namedBuilder = NamedBuilder.class.cast(previous);
+            name = namedBuilder.name;
+            previous = namedBuilder.builder;
+        } else {
+            name = null;
+        }
+
+        if (JsonArrayBuilder.class.isInstance(last)) {
+            final JsonArrayBuilder array = JsonArrayBuilder.class.cast(last);
+            if (JsonArrayBuilder.class.isInstance(previous)) {
+                arrayBuilder = JsonArrayBuilder.class.cast(previous);
+                objectBuilder = null;
+                arrayBuilder.add(array);
+            } else if (JsonObjectBuilder.class.isInstance(previous)) {
+                objectBuilder = JsonObjectBuilder.class.cast(previous);
+                arrayBuilder = null;
+                objectBuilder.add(name, array);
+            } else {
+                throw new IllegalArgumentException("Unsupported previous builder: " + previous);
+            }
+        } else if (JsonObjectBuilder.class.isInstance(last)) {
+            final JsonObjectBuilder object = JsonObjectBuilder.class.cast(last);
+            if (JsonArrayBuilder.class.isInstance(previous)) {
+                arrayBuilder = JsonArrayBuilder.class.cast(previous);
+                objectBuilder = null;
+                arrayBuilder.add(object);
+            } else if (JsonObjectBuilder.class.isInstance(previous)) {
+                objectBuilder = JsonObjectBuilder.class.cast(previous);
+                arrayBuilder = null;
+                objectBuilder.add(name, object);
+            } else {
+                throw new IllegalArgumentException("Unsupported previous builder: " + previous);
+            }
+        } else if (NamedBuilder.class.isInstance(last)) {
+            final NamedBuilder<?> namedBuilder = NamedBuilder.class.cast(last);
+            if (JsonObjectBuilder.class.isInstance(previous)) {
+                objectBuilder = JsonObjectBuilder.class.cast(previous);
+                if (JsonArrayBuilder.class.isInstance(namedBuilder.builder)) {
+                    objectBuilder.add(namedBuilder.name, JsonArrayBuilder.class.cast(namedBuilder.builder));
+                    arrayBuilder = null;
+                } else if (JsonObjectBuilder.class.isInstance(namedBuilder.builder)) {
+                    objectBuilder.add(namedBuilder.name, JsonObjectBuilder.class.cast(namedBuilder.builder));
+                    arrayBuilder = null;
+                } else {
+                    throw new IllegalArgumentException("Unsupported previous builder: " + previous);
+                }
+            } else {
+                throw new IllegalArgumentException("Unsupported previous builder, expected object builder: " + previous);
+            }
+        } else {
+            throw new IllegalArgumentException("Unsupported previous builder: " + previous);
+        }
+        return this;
+    }
+
+    @Override
+    public void flush() {
+        // no-op
+    }
+
+    @Override
+    public void close() {
+        flush();
+    }
+
+    public JsonValue getResult() {
+        final Object last = builders.getLast();
+        if (JsonArrayBuilder.class.isInstance(last)) {
+            return JsonArrayBuilder.class.cast(last).build();
+        }
+        if (JsonObjectBuilder.class.isInstance(last)) {
+            return JsonObjectBuilder.class.cast(last).build();
+        }
+        throw new IllegalArgumentException("Nothing prepared or wrongly prepared");
+    }
+
+    private static class NamedBuilder<T> {
+        private final T builder;
+        private final String name;
+
+        private NamedBuilder(final T builder, final String name) {
+            this.builder = builder;
+            this.name = name;
+        }
+    }
+}
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 da28ae7..77b22b6 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
@@ -18,14 +18,9 @@
  */
 package org.apache.johnzon.mapper;
 
-import org.apache.johnzon.mapper.internal.JsonPointerTracker;
-import org.apache.johnzon.mapper.reflection.JohnzonCollectionType;
+import static java.util.Arrays.asList;
+import static org.apache.johnzon.mapper.internal.Streams.noClose;
 
-import javax.json.JsonReader;
-import javax.json.JsonReaderFactory;
-import javax.json.JsonValue;
-import javax.json.stream.JsonGenerator;
-import javax.json.stream.JsonGeneratorFactory;
 import java.io.Closeable;
 import java.io.IOException;
 import java.io.InputStream;
@@ -38,13 +33,26 @@ import java.io.Writer;
 import java.lang.reflect.Array;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.math.BigInteger;
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Collection;
 
-import static java.util.Arrays.asList;
-import static org.apache.johnzon.mapper.internal.Streams.noClose;
+import javax.json.JsonArray;
+import javax.json.JsonBuilderFactory;
+import javax.json.JsonObject;
+import javax.json.JsonReader;
+import javax.json.JsonReaderFactory;
+import javax.json.JsonStructure;
+import javax.json.JsonValue;
+import javax.json.spi.JsonProvider;
+import javax.json.stream.JsonGenerator;
+import javax.json.stream.JsonGeneratorFactory;
+
+import org.apache.johnzon.mapper.internal.JsonPointerTracker;
+import org.apache.johnzon.mapper.reflection.JohnzonCollectionType;
 
 public class Mapper implements Closeable {
 
@@ -52,14 +60,19 @@ public class Mapper implements Closeable {
     protected final Mappings mappings;
     protected final JsonReaderFactory readerFactory;
     protected final JsonGeneratorFactory generatorFactory;
+    protected final JsonBuilderFactory builderFactory;
+    protected final JsonProvider provider;
     protected final ReaderHandler readerHandler;
     protected final Collection<Closeable> closeables;
     protected final Charset charset;
 
-    Mapper(final JsonReaderFactory readerFactory, final JsonGeneratorFactory generatorFactory, MapperConfig config,
-                  final Collection<Closeable> closeables) {
+    Mapper(final JsonReaderFactory readerFactory, final JsonGeneratorFactory generatorFactory,
+           final JsonBuilderFactory builderFactory, final JsonProvider provider,
+           final MapperConfig config, final Collection<Closeable> closeables) {
         this.readerFactory = readerFactory;
         this.generatorFactory = generatorFactory;
+        this.builderFactory = builderFactory;
+        this.provider = provider;
         this.config = config;
         this.mappings = new Mappings(config);
         this.readerHandler = ReaderHandler.create(readerFactory);
@@ -102,6 +115,43 @@ public class Mapper implements Closeable {
         }
     }
 
+    public JsonValue toStructure(final Object object) {
+        if (object == null) {
+            return JsonValue.NULL;
+        }
+        if (JsonStructure.class.isInstance(object)) {
+            return JsonStructure.class.cast(object);
+        }
+        if (Boolean.class.isInstance(object)) {
+            return Boolean.class.cast(object) ? JsonValue.TRUE : JsonValue.FALSE;
+        }
+        if (String.class.isInstance(object)) {
+            return provider.createValue(String.class.cast(object));
+        }
+        if (Double.class.isInstance(object)) {
+            return provider.createValue(Double.class.cast(object));
+        }
+        if (Float.class.isInstance(object)) {
+            return provider.createValue(Float.class.cast(object));
+        }
+        if (Long.class.isInstance(object)) {
+            return provider.createValue(Long.class.cast(object));
+        }
+        if (Integer.class.isInstance(object)) {
+            return provider.createValue(Integer.class.cast(object));
+        }
+        if (BigDecimal.class.isInstance(object)) {
+            return provider.createValue(BigDecimal.class.cast(object));
+        }
+        if (BigInteger.class.isInstance(object)) {
+            return provider.createValue(BigInteger.class.cast(object));
+        }
+        final JsonObjectGenerator objectGenerator = new JsonObjectGenerator(builderFactory);
+        writeObject(object, objectGenerator, null,
+                isDeduplicateObjects(object.getClass()) ? new JsonPointerTracker(null, "/") : null);
+        return objectGenerator.getResult();
+    }
+
     public void writeObject(final Object object, final Writer stream) {
         if (JsonValue.class.isInstance(object)
                 || Boolean.class.isInstance(object) || String.class.isInstance(object) || Number.class.isInstance(object)
@@ -179,6 +229,34 @@ public class Mapper implements Closeable {
         return writer.toString();
     }
 
+    public <T> T readObject(final JsonStructure value, final Type clazz) {
+        return new MappingParserImpl(config, mappings, new JsonReader() {
+            @Override
+            public JsonStructure read() {
+                return value;
+            }
+
+            @Override
+            public JsonValue readValue() {
+                return value;
+            }
+
+            @Override
+            public JsonObject readObject() {
+                return value.asJsonObject();
+            }
+
+            @Override
+            public JsonArray readArray() {
+                return value.asJsonArray();
+            }
+
+            @Override
+            public void close() {
+                // no-op
+            }
+        }, isDedup(clazz)).readObject(clazz);
+    }
 
     public <T> T readObject(final String string, final Type clazz) {
         return readObject(new StringReader(string), clazz);
@@ -247,11 +325,14 @@ public class Mapper implements Closeable {
 
 
     private <T> T mapObject(final Type clazz, final JsonReader reader) {
-        boolean dedup = false;
+        return new MappingParserImpl(config, mappings, reader, isDedup(clazz)).readObject(clazz);
+    }
+
+    private boolean isDedup(final Type clazz) {
         if (clazz instanceof Class) {
-            dedup = isDeduplicateObjects((Class) clazz);
+            return isDeduplicateObjects((Class) clazz);
         }
-        return new MappingParserImpl(config, mappings, reader, dedup).readObject(clazz);
+        return false;
     }
 
 
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 5d6debb..258ce5f 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
@@ -19,6 +19,7 @@
 package org.apache.johnzon.mapper;
 
 import static java.util.Arrays.asList;
+import static java.util.Collections.emptyMap;
 import static java.util.Locale.ROOT;
 
 import org.apache.johnzon.core.JsonParserFactoryImpl;
@@ -47,6 +48,7 @@ import org.apache.johnzon.mapper.converter.URLConverter;
 import org.apache.johnzon.mapper.internal.AdapterKey;
 import org.apache.johnzon.mapper.internal.ConverterAdapter;
 
+import javax.json.JsonBuilderFactory;
 import javax.json.JsonReaderFactory;
 import javax.json.spi.JsonProvider;
 import javax.json.stream.JsonGenerator;
@@ -106,6 +108,8 @@ public class MapperBuilder {
 
     private JsonReaderFactory readerFactory;
     private JsonGeneratorFactory generatorFactory;
+    private JsonProvider provider;
+    private JsonBuilderFactory builderFactory;
     private boolean supportHiddenAccess = true;
     private int maxSize = -1;
     private int bufferSize = -1;
@@ -142,7 +146,13 @@ public class MapperBuilder {
 
     public Mapper build() {
         if (readerFactory == null || generatorFactory == null) {
-            final JsonProvider provider = JsonProvider.provider();
+            final JsonProvider provider;
+            if (this.provider != null) {
+                provider = this.provider;
+            } else {
+                provider = JsonProvider.provider();
+                this.provider = provider;
+            }
             final Map<String, Object> config = new HashMap<String, Object>();
             if (bufferStrategy != null) {
                 config.put(JsonParserFactoryImpl.BUFFER_STRATEGY, bufferStrategy);
@@ -171,6 +181,9 @@ public class MapperBuilder {
             if (readerFactory == null) {
                 readerFactory = provider.createReaderFactory(config);
             }
+            if (builderFactory == null) {
+                builderFactory = provider.createBuilderFactory(emptyMap());
+            }
         }
 
         if (accessMode == null) {
@@ -233,7 +246,7 @@ public class MapperBuilder {
         }
 
         return new Mapper(
-                readerFactory, generatorFactory,
+                readerFactory, generatorFactory, builderFactory, provider,
                 new MapperConfig(
                         adapters, objectConverterWriters, objectConverterReaders,
                         version, close,
@@ -345,6 +358,16 @@ public class MapperBuilder {
         return this;
     }
 
+    public MapperBuilder setProvider(final JsonProvider provider) {
+        this.provider = provider;
+        return this;
+    }
+
+    public MapperBuilder setBuilderFactory(final JsonBuilderFactory builderFactory) {
+        this.builderFactory = builderFactory;
+        return this;
+    }
+
     public MapperBuilder setDoCloseOnStreams(final boolean doCloseOnStreams) {
         this.close = doCloseOnStreams;
         return this;
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 3e7672e..912fbb2 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
@@ -23,6 +23,7 @@ import org.apache.johnzon.mapper.reflection.JohnzonCollectionType;
 import org.apache.johnzon.mapper.reflection.JohnzonParameterizedType;
 import org.junit.Assert;
 import org.junit.Test;
+import org.superbiz.MultiStructureObject;
 
 import java.beans.ConstructorProperties;
 import java.io.ByteArrayInputStream;
@@ -53,6 +54,10 @@ import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import javax.json.JsonValue;
+
 public class MapperTest {
     private static final String BIG_OBJECT_STR = "{" + "\"name\":\"the string\"," + "\"intVal\":56," + "\"longnumber\":118,"
             + "\"bool\":true," + "\"nested\":{" + "\"name\":\"another value\"," + "\"intVal\":97," + "\"longnumber\":34" + "},"
@@ -62,6 +67,47 @@ public class MapperTest {
             + "\"primitives\":[1,2,3,4,5]," + "\"collectionWrapper\":[1,2,3,4,5]," + "\"map\":{\"uno\":true,\"duos\":false}" + "}";
 
     @Test
+    public void mapToJsonValue() {
+        final Child object = new Child();
+        object.children = Collections.singletonList("first");
+        object.a = 5;
+        object.b = 6;
+        object.c = 7;
+        final JsonValue structure = new MapperBuilder().build().toStructure(object);
+        assertEquals(JsonValue.ValueType.OBJECT, structure.getValueType());
+        final JsonObject jsonObject = structure.asJsonObject();
+        assertEquals(4, jsonObject.size());
+        assertEquals(5, jsonObject.getInt("a"));
+        assertEquals(6, jsonObject.getInt("b"));
+        assertEquals(7, jsonObject.getInt("c"));
+
+        final JsonArray children = jsonObject.getJsonArray("children");
+        assertEquals(1, children.size());
+        assertEquals("first", children.getString(0));
+    }
+
+    @Test
+    public void mapToJsonValueComplex() {
+        final MultiStructureObject.Nested n1 = new MultiStructureObject.Nested();
+        n1.number = 3;
+
+        final MultiStructureObject object = new MultiStructureObject();
+        object.names = asList("first", "second");
+        object.data = "some";
+        object.polymorphic = new HashMap<>();
+        object.polymorphic.put("a", 1);
+        object.polymorphic.put("b", "2");
+        object.nesteds = Collections.singletonList(n1);
+        object.nestedMap = Collections.singletonMap("n1", n1);
+
+        final JsonValue structure = new MapperBuilder().setAttributeOrder(String.CASE_INSENSITIVE_ORDER).build().toStructure(object);
+        assertEquals(JsonValue.ValueType.OBJECT, structure.getValueType());
+        final JsonObject jsonObject = structure.asJsonObject();
+        assertEquals("{\"data\":\"some\",\"names\":[\"first\",\"second\"],\"nestedMap\":{\"n1\":{\"number\":3}}," +
+                "\"nesteds\":[{\"number\":3}],\"polymorphic\":{\"a\":1,\"b\":\"2\"}}", jsonObject.toString());
+    }
+
+    @Test
     public void ignoreAllStrategy() {
         final StringWriter writer = new StringWriter();
         final Child object = new Child();
diff --git a/johnzon-mapper/src/test/java/org/superbiz/MultiStructureObject.java b/johnzon-mapper/src/test/java/org/superbiz/MultiStructureObject.java
new file mode 100644
index 0000000..3207c43
--- /dev/null
+++ b/johnzon-mapper/src/test/java/org/superbiz/MultiStructureObject.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.superbiz;
+
+import java.util.Collection;
+import java.util.Map;
+
+public class MultiStructureObject {
+    public Map<String, Object> polymorphic;
+    public String data;
+    public Collection<Nested> nesteds;
+    public Map<String, Nested> nestedMap;
+    public Collection<String> names;
+
+    public static class Nested {
+        public int number;
+    }
+}
diff --git a/src/site/markdown/index.md b/src/site/markdown/index.md
index 7ee99a3..db026f0 100644
--- a/src/site/markdown/index.md
+++ b/src/site/markdown/index.md
@@ -298,7 +298,26 @@ It fully reuses the JSON-B as API.
 
 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. 
+to use for deserialization.
+
+#### Integration with `JsonValue`
+
+You can use some optimization to map a `JsonObject` to a POJO using Johnzon `JsonValueReader` and `JsonValueWriter`:
+
+<pre class="prettyprint linenums"><![CDATA[
+final JsonValueReader<Simple> reader = new JsonValueReader<>(Json.createObjectBuilder().add("value", "simple").build());
+final Jsonb jsonb = getJohnsonJsonb();
+final Simple simple = jsonb.fromJson(reader, SomeModel.class);
+]]></pre>
+
+<pre class="prettyprint linenums"><![CDATA[
+final JsonValueWriter writer = new JsonValueWriter();
+final Jsonb jsonb = getJohnsonJsonb();
+jsonb.toJson(object, writer);
+final JsonObject jsonObject = writer.getObject();
+]]></pre>
+
+These two example will not use any IO and directly map the `JsonValue` to/from a POJO.
 
 ### Websocket