Support conversion of JsonObject and JsonArray from/to java pojo classes

Signed-off-by: Roman Grigoriadi <roman.grigoriadi@oracle.com>
diff --git a/src/main/java/org/eclipse/yasson/YassonJsonb.java b/src/main/java/org/eclipse/yasson/YassonJsonb.java
index ed3643a..570cd84 100644
--- a/src/main/java/org/eclipse/yasson/YassonJsonb.java
+++ b/src/main/java/org/eclipse/yasson/YassonJsonb.java
@@ -9,6 +9,7 @@
  ******************************************************************************/
 package org.eclipse.yasson;
 
+import javax.json.JsonStructure;
 import javax.json.bind.JsonbException;
 import javax.json.stream.JsonGenerator;
 import javax.json.stream.JsonParser;
@@ -71,6 +72,47 @@
     <T> T fromJson(JsonParser jsonParser, Type runtimeType) throws JsonbException;
 
     /**
+     * Reads a {@link JsonStructure} and and converts it into
+     * resulting java content tree.
+     *
+     * @param jsonStructure
+     *      {@link JsonStructure} to be used as a source for conversion.
+     * @param type
+     *      Type of the content tree's root object.
+     * @param <T>
+     *      Type of the content tree's root object.
+     *
+     * @return the newly created root object of the java content tree
+     *
+     * @throws JsonbException
+     *     If any unexpected error(s) occur(s) during conversion.
+     * @throws NullPointerException
+     *      If any of the parameters is {@code null}.
+     */
+    <T> T fromJsonStructure(JsonStructure jsonStructure, Class<T> type) throws JsonbException;
+
+    /**
+     * Reads a {@link JsonStructure} and and converts it into
+     * resulting java content tree.
+     *
+     * @param jsonStructure
+     *      {@link JsonStructure} to be used as a source for conversion.
+     * @param runtimeType
+     *      Runtime type of the content tree's root object.
+     * @param <T>
+     *      Type of the content tree's root object.
+     *
+     * @return the newly created root object of the java content tree
+     *
+     * @throws JsonbException
+     *     If any unexpected error(s) occur(s) during deserialization.
+     * @throws NullPointerException
+     *      If any of the parameters is {@code null}.
+     */
+    <T> T fromJsonStructure(JsonStructure jsonStructure, Type runtimeType) throws JsonbException;
+
+
+    /**
      * Writes the object content tree with a specified {@link JsonGenerator}.
      * Provided json generator must be fully initialized, no further configurations are applied.
      *
@@ -111,4 +153,41 @@
      * @since JSON Binding 1.0
      */
     void toJson(Object object, Type runtimeType, JsonGenerator jsonGenerator) throws JsonbException;
+
+    /**
+     * Serializes the object content tree to a {@link javax.json.JsonStructure}.
+     *
+     * @param object
+     *      The object content tree to be serialized.
+     *
+     * @return The {@link JsonStructure} serialized from java content tree.
+     *
+     * @throws JsonbException If any unexpected problem occurs during the
+     * serialization.
+     * @throws NullPointerException
+     *      If any of the parameters is {@code null}.
+     *
+     * @since JSON Binding 1.0
+     */
+    JsonStructure toJsonStructure(Object object) throws JsonbException;
+
+    /**
+     * Serializes the object content tree to a {@link javax.json.JsonStructure}.
+     *
+     * @param object
+     *      The object content tree to be serialized.
+     *
+     * @param runtimeType
+     *      Runtime type of the content tree's root object.
+     *
+     * @return The {@link JsonStructure} serialized from java content tree.
+     *
+     * @throws JsonbException If any unexpected problem occurs during the
+     * serialization.
+     * @throws NullPointerException
+     *      If any of the parameters is {@code null}.
+     *
+     * @since JSON Binding 1.0
+     */
+    JsonStructure toJsonStructure (Object object, Type runtimeType) throws JsonbException;
 }
diff --git a/src/main/java/org/eclipse/yasson/internal/JsonBinding.java b/src/main/java/org/eclipse/yasson/internal/JsonBinding.java
index 6e7a53d..f8b3d75 100644
--- a/src/main/java/org/eclipse/yasson/internal/JsonBinding.java
+++ b/src/main/java/org/eclipse/yasson/internal/JsonBinding.java
@@ -13,15 +13,23 @@
 package org.eclipse.yasson.internal;
 
 import org.eclipse.yasson.YassonJsonb;
+import org.eclipse.yasson.internal.jsonstructure.JsonGeneratorToStructureAdapter;
+import org.eclipse.yasson.internal.jsonstructure.JsonStructureToParserAdapter;
 import org.eclipse.yasson.internal.properties.MessageKeys;
 import org.eclipse.yasson.internal.properties.Messages;
 
+import javax.json.JsonStructure;
 import javax.json.bind.JsonbConfig;
 import javax.json.bind.JsonbException;
 import javax.json.spi.JsonProvider;
 import javax.json.stream.JsonGenerator;
 import javax.json.stream.JsonParser;
-import java.io.*;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
 import java.lang.reflect.Type;
 import java.nio.charset.Charset;
 import java.util.HashMap;
@@ -85,6 +93,18 @@
         return deserialize(type, inputStreamParser(stream), unmarshaller);
     }
 
+    @Override
+    public <T> T fromJsonStructure(JsonStructure jsonStructure, Class<T> type) throws JsonbException {
+        JsonParser parser = new JsonbRiParser(new JsonStructureToParserAdapter(jsonStructure));
+        return deserialize(type, parser, new Unmarshaller(jsonbContext));
+    }
+
+    @Override
+    public <T> T fromJsonStructure(JsonStructure jsonStructure, Type runtimeType) throws JsonbException {
+        JsonParser parser = new JsonbRiParser(new JsonStructureToParserAdapter(jsonStructure));
+        return deserialize(runtimeType, parser, new Unmarshaller(jsonbContext));
+    }
+
     private JsonParser inputStreamParser(InputStream stream) {
         return new JsonbRiParser(jsonbContext.getJsonProvider().createParserFactory(createJsonpProperties(jsonbContext.getConfig()))
                 .createParser(stream,
@@ -163,6 +183,22 @@
         marshaller.marshallWithoutClose(object, jsonGenerator);
     }
 
+    @Override
+    public JsonStructure toJsonStructure(Object object) throws JsonbException {
+        JsonGeneratorToStructureAdapter structureGenerator = new JsonGeneratorToStructureAdapter(jsonbContext.getJsonProvider());
+        final Marshaller marshaller = new Marshaller(jsonbContext);
+        marshaller.marshall(object, structureGenerator);
+        return structureGenerator.getRootStructure();
+    }
+
+    @Override
+    public JsonStructure toJsonStructure(Object object, Type runtimeType) throws JsonbException {
+        JsonGeneratorToStructureAdapter structureGenerator = new JsonGeneratorToStructureAdapter(jsonbContext.getJsonProvider());
+        final Marshaller marshaller = new Marshaller(jsonbContext, runtimeType);
+        marshaller.marshall(object, structureGenerator);
+        return structureGenerator.getRootStructure();
+    }
+
     private JsonGenerator streamGenerator(OutputStream stream) {
         Map<String, ?> factoryProperties = createJsonpProperties(jsonbContext.getConfig());
         final String encoding = (String) jsonbContext.getConfig().getProperty(JsonbConfig.ENCODING).orElse("UTF-8");
diff --git a/src/main/java/org/eclipse/yasson/internal/JsonbRiParser.java b/src/main/java/org/eclipse/yasson/internal/JsonbRiParser.java
index 29e2183..18e31d2 100644
--- a/src/main/java/org/eclipse/yasson/internal/JsonbRiParser.java
+++ b/src/main/java/org/eclipse/yasson/internal/JsonbRiParser.java
@@ -25,6 +25,7 @@
 import java.math.BigDecimal;
 import java.util.Arrays;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Stack;
 import java.util.stream.Stream;
 
@@ -76,6 +77,7 @@
         }
 
         private void setLastKeyName(String lastKeyName) {
+            Objects.requireNonNull(lastKeyName);
             this.lastKeyName = lastKeyName;
         }
 
diff --git a/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonArrayBuilder.java b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonArrayBuilder.java
new file mode 100644
index 0000000..e977b2f
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonArrayBuilder.java
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
+ * which accompanies this distribution.
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ ******************************************************************************/
+package org.eclipse.yasson.internal.jsonstructure;
+
+import javax.json.JsonArray;
+import javax.json.JsonStructure;
+import javax.json.JsonValue;
+import javax.json.spi.JsonProvider;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * Builds {@link JsonArray}. Delegates to {@link javax.json.JsonArrayBuilder}.
+ */
+class JsonArrayBuilder extends JsonStructureBuilder {
+
+    private final javax.json.JsonArrayBuilder arrayBuilder;
+
+    /**
+     * Create instance with cached provider.
+     * @param provider Json provider to create JsonArrayBuilder on.
+     */
+    JsonArrayBuilder(JsonProvider provider) {
+        this.arrayBuilder = provider.createArrayBuilder();
+    }
+
+    @Override
+    JsonArray build() {
+        return arrayBuilder.build();
+    }
+
+    @Override
+    void write(JsonValue value) {
+        arrayBuilder.add(value);
+    }
+
+    @Override
+    void write(String value) {
+        arrayBuilder.add(value);
+    }
+
+    @Override
+    void write(BigDecimal value) {
+        arrayBuilder.add(value);
+    }
+
+    @Override
+    void write(BigInteger value) {
+        arrayBuilder.add(value);
+    }
+
+    @Override
+    void write(int value) {
+        arrayBuilder.add(value);
+    }
+
+    @Override
+    void write(long value) {
+        arrayBuilder.add(value);
+    }
+
+    @Override
+    void write(double value) {
+        arrayBuilder.add(value);
+    }
+
+    @Override
+    void write(boolean value) {
+        arrayBuilder.add(value);
+    }
+
+    @Override
+    void writeNull() {
+        arrayBuilder.addNull();
+    }
+
+    @Override
+    void put(JsonStructure structure) {
+        arrayBuilder.add(structure);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonArrayIterator.java b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonArrayIterator.java
new file mode 100644
index 0000000..c6d0b0f
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonArrayIterator.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
+ * which accompanies this distribution.
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ ******************************************************************************/
+package org.eclipse.yasson.internal.jsonstructure;
+
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+import javax.json.JsonArray;
+import javax.json.JsonString;
+import javax.json.JsonValue;
+import javax.json.bind.JsonbException;
+import javax.json.stream.JsonParser;
+import java.util.Iterator;
+
+/**
+ * Iterates over {@link JsonArray}.
+ */
+public class JsonArrayIterator extends JsonStructureIterator {
+
+    private final Iterator<JsonValue> valueIterator;
+
+    private JsonValue currentValue;
+
+    public JsonArrayIterator(JsonArray jsonArray) {
+        this.valueIterator = jsonArray.iterator();
+    }
+
+    /**
+     * After {@link JsonParser.Event} END_ARRAY is returned from next() iterator is removed from the stack
+     * @return always true
+     */
+    @Override
+    public boolean hasNext() {
+        return true;
+    }
+
+    @Override
+    public JsonParser.Event next() {
+        if (valueIterator.hasNext()) {
+            currentValue = valueIterator.next();
+            return getValueEvent(currentValue);
+        }
+        return JsonParser.Event.END_ARRAY;
+    }
+
+    @Override
+    JsonValue getValue() {
+        return currentValue;
+    }
+
+    @Override
+    JsonbException createIncompatibleValueError() {
+        return new JsonbException(Messages.getMessage(MessageKeys.NUMBER_INCOMPATIBLE_VALUE_TYPE_ARRAY, getValue().getValueType()));
+    }
+
+    @Override
+    String getString() {
+        if (currentValue instanceof JsonString) {
+            return ((JsonString) currentValue).getString();
+        }
+        return currentValue.toString();
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonGeneratorToStructureAdapter.java b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonGeneratorToStructureAdapter.java
new file mode 100644
index 0000000..d32d42b
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonGeneratorToStructureAdapter.java
@@ -0,0 +1,228 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
+ * which accompanies this distribution.
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ ******************************************************************************/
+package org.eclipse.yasson.internal.jsonstructure;
+
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+import javax.json.JsonStructure;
+import javax.json.JsonValue;
+import javax.json.bind.JsonbException;
+import javax.json.spi.JsonProvider;
+import javax.json.stream.JsonGenerator;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Stack;
+
+/**
+ * Adapter for {@link JsonGenerator}, that builds a {@link JsonStructure} content tree instead of JSON text.
+ *
+ * Yasson and jsonb API components are using {@link JsonGenerator} as its output API.
+ * This adapter allows serialization of java content tree into {@link JsonStructure} using same components
+ * as when generating JSON text.
+ */
+public class JsonGeneratorToStructureAdapter implements JsonGenerator {
+
+    private final Stack<JsonStructureBuilder> builders;
+
+    private JsonStructure root;
+
+    private final JsonProvider provider;
+
+    /**
+     * Default constructor, jsonp builders are created internally.
+     *
+     * @param provider Cached json provider to create builders on.
+     */
+    public JsonGeneratorToStructureAdapter(JsonProvider provider) {
+        this.builders = new Stack<>();
+        this.provider = provider;
+    }
+
+    @Override
+    public JsonGenerator writeStartObject() {
+        builders.push(new JsonObjectBuilder(provider));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartObject(String name) {
+        getJsonObjectBuilder(name).writeKey(name);
+        builders.push(new JsonObjectBuilder(provider));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeKey(String name) {
+        getJsonObjectBuilder(name).writeKey(name);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartArray() {
+        builders.push(new JsonArrayBuilder(provider));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartArray(String name) {
+        getJsonObjectBuilder(name).writeKey(name);
+        builders.push(new JsonArrayBuilder(provider));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, JsonValue value) {
+        getJsonObjectBuilder(name).write(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, String value) {
+        getJsonObjectBuilder(name).write(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, BigInteger value) {
+        getJsonObjectBuilder(name).write(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, BigDecimal value) {
+        getJsonObjectBuilder(name).write(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, int value) {
+        getJsonObjectBuilder(name).write(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, long value) {
+        getJsonObjectBuilder(name).write(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, double value) {
+        getJsonObjectBuilder(name).write(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, boolean value) {
+        getJsonObjectBuilder(name).write(name, value);
+        return this;
+    }
+
+    private JsonObjectBuilder getJsonObjectBuilder(String keyName) {
+        JsonStructureBuilder current = builders.peek();
+        if (!(current instanceof JsonObjectBuilder)) {
+            throw new JsonbException(Messages.getMessage(
+                    MessageKeys.INTERNAL_ERROR, "Can't write key [" + keyName + "] into " + current.getClass()));
+        }
+        return (JsonObjectBuilder) current;
+    }
+
+    @Override
+    public JsonGenerator writeNull(String name) {
+        getJsonObjectBuilder(name).writeNull(name);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeEnd() {
+        JsonStructureBuilder builder = builders.pop();
+        JsonStructure structure = builder.build();
+        if (builders.isEmpty()) {
+            this.root = structure;
+        } else {
+            builders.peek().put(structure);
+        }
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(JsonValue value) {
+        builders.peek().write(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String value) {
+        builders.peek().write(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(BigDecimal value) {
+        builders.peek().write(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(BigInteger value) {
+        builders.peek().write(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(int value) {
+        builders.peek().write(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(long value) {
+        builders.peek().write(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(double value) {
+        builders.peek().write(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(boolean value) {
+        builders.peek().write(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeNull() {
+        builders.peek().writeNull();
+        return this;
+    }
+
+    @Override
+    public void close() {
+        //noop
+    }
+
+    @Override
+    public void flush() {
+        //noop
+    }
+
+    /**
+     * Root structure wrapping all data.
+     * @return root JsonStructure.
+     */
+    public JsonStructure getRootStructure() {
+        return root;
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonObjectBuilder.java b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonObjectBuilder.java
new file mode 100644
index 0000000..dbebcbc
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonObjectBuilder.java
@@ -0,0 +1,203 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
+ * which accompanies this distribution.
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ ******************************************************************************/
+package org.eclipse.yasson.internal.jsonstructure;
+
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+import javax.json.JsonStructure;
+import javax.json.JsonValue;
+import javax.json.bind.JsonbException;
+import javax.json.spi.JsonProvider;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * Builds {@link javax.json.JsonObject} delegates to {@link javax.json.JsonObjectBuilder}, caches key when
+ * written without a value.
+ */
+class JsonObjectBuilder extends JsonStructureBuilder {
+
+    private final javax.json.JsonObjectBuilder builder;
+
+    private String nextKey;
+
+    /**
+     * Create instance with cached provider.
+     * @param provider Json provider to create JsonObjectBuilder on.
+     */
+    JsonObjectBuilder(JsonProvider provider) {
+        this.builder = provider.createObjectBuilder();
+    }
+
+    @Override
+    JsonStructure build() {
+        return builder.build();
+    }
+
+    @Override
+    void put(JsonStructure structure) {
+        builder.add(getNextKey(), structure);
+    }
+
+    /**
+     * Puts another {@link JsonStructure} into current using provided key.
+     * @param name key to put JsonStructure under.
+     * @param structure JsonStructure to put.
+     */
+    void put(String name, JsonStructure structure) {
+        builder.add(name, structure);
+    }
+
+    @Override
+    void write(JsonValue value) {
+        builder.add(getNextKey(), value);
+    }
+
+    @Override
+    void write(String value) {
+        builder.add(getNextKey(), value);
+    }
+
+    @Override
+    void write(BigDecimal value) {
+        builder.add(getNextKey(), value);
+    }
+
+    @Override
+    void write(BigInteger value) {
+        builder.add(getNextKey(), value);
+    }
+
+    @Override
+    void write(int value) {
+        builder.add(getNextKey(), value);
+    }
+
+    @Override
+    void write(long value) {
+        builder.add(getNextKey(), value);
+    }
+
+    @Override
+    void write(double value) {
+        builder.add(getNextKey(), value);
+    }
+
+    @Override
+    void write(boolean value) {
+        builder.add(getNextKey(), value);
+    }
+
+    @Override
+    void writeNull() {
+        builder.addNull(getNextKey());
+    }
+
+    /**
+     * Write a key-value pair into current {@link javax.json.JsonObject}.
+     * @param name Key name to write value with.
+     * @param value A value to write.
+     */
+    void write(String name, JsonValue value) {
+        builder.add(name, value);
+    }
+
+    /**
+     * Write a key-value pair into current {@link javax.json.JsonObject}.
+     * @param name Key name to write value with.
+     * @param value A value to write.
+     */
+    void write(String name, String value) {
+        builder.add(name, value);
+    }
+
+    /**
+     * Write a key-value pair into current {@link javax.json.JsonObject}.
+     * @param name Key name to write value with.
+     * @param value A value to write.
+     */
+    void write(String name, BigDecimal value) {
+        builder.add(name, value);
+    }
+
+    /**
+     * Write a key-value pair into current {@link javax.json.JsonObject}.
+     * @param name Key name to write value with.
+     * @param value A value to write.
+     */
+    void write(String name, BigInteger value) {
+        builder.add(name, value);
+    }
+
+    /**
+     * Write a key-value pair into current {@link javax.json.JsonObject}.
+     * @param name Key name to write value with.
+     * @param value A value to write.
+     */
+    void write(String name, int value) {
+        builder.add(name, value);
+    }
+
+    /**
+     * Write a key-value pair into current {@link javax.json.JsonObject}.
+     * @param name Key name to write value with.
+     * @param value A value to write.
+     */
+    void write(String name, long value) {
+        builder.add(name, value);
+    }
+
+    /**
+     * Write a key-value pair into current {@link javax.json.JsonObject}.
+     * @param name Key name to write value with.
+     * @param value A value to write.
+     */
+    void write(String name, double value) {
+        builder.add(name, value);
+    }
+
+    /**
+     * Write a key-value pair into current {@link javax.json.JsonObject}.
+     * @param name Key name to write value with.
+     * @param value A value to write.
+     */
+    void write(String name, boolean value) {
+        builder.add(name, value);
+    }
+
+    /**
+     * Write a null into current {@link javax.json.JsonObject} with a given key.
+     * @param name Key name to write null with.
+     */
+    void writeNull(String name) {
+        builder.addNull(name);
+    }
+
+    /**
+     * Store a key for putting next value into built {@link javax.json.JsonObject}.
+     * @param key Key to store.
+     */
+    void writeKey(String key) {
+        this.nextKey = key;
+    }
+
+
+    private String getNextKey() {
+        if (nextKey == null) {
+            throw new JsonbException(Messages.getMessage(MessageKeys.INTERNAL_ERROR,
+                    "Can't write a value without key name"));
+        }
+        String key = nextKey;
+        nextKey = null;
+        return key;
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonObjectIterator.java b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonObjectIterator.java
new file mode 100644
index 0000000..41917ed
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonObjectIterator.java
@@ -0,0 +1,127 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
+ * which accompanies this distribution.
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ ******************************************************************************/
+package org.eclipse.yasson.internal.jsonstructure;
+
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+import javax.json.JsonObject;
+import javax.json.JsonValue;
+import javax.json.bind.JsonbException;
+import javax.json.stream.JsonParser;
+import java.util.Iterator;
+
+/**
+ * Iterates over {@link JsonObject} managing internal state.
+ */
+public class JsonObjectIterator extends JsonStructureIterator {
+
+    /**
+     * Location pointer.
+     */
+    public enum State {
+        START,
+        KEY,
+        VALUE,
+        END
+    }
+
+    private final JsonObject jsonObject;
+
+    private final Iterator<String> keyIterator;
+
+    private String currentKey;
+
+    private State state = State.START;
+
+
+    JsonObjectIterator(JsonObject jsonObject) {
+        this.jsonObject = jsonObject;
+        this.keyIterator = jsonObject.keySet().iterator();
+    }
+
+
+    private void nextKey() {
+        if (!keyIterator.hasNext()) {
+            throw new JsonbException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "Object is empty"));
+        }
+        currentKey = keyIterator.next();
+    }
+
+    @Override
+    public JsonParser.Event next() {
+        switch (state) {
+            case START:
+                if (keyIterator.hasNext()) {
+                    nextKey();
+                    setState(JsonObjectIterator.State.KEY);
+                    return JsonParser.Event.KEY_NAME;
+                } else {
+                    setState(State.END);
+                    return JsonParser.Event.END_OBJECT;
+                }
+            case KEY:
+                setState(JsonObjectIterator.State.VALUE);
+                JsonValue value = getValue();
+                return getValueEvent(value);
+            case VALUE:
+                if (keyIterator.hasNext()) {
+                    nextKey();
+                    setState(JsonObjectIterator.State.KEY);
+                    return JsonParser.Event.KEY_NAME;
+                }
+                setState(State.END);
+                return JsonParser.Event.END_OBJECT;
+            default:
+                throw new JsonbException("Illegal state");
+        }
+
+    }
+
+    @Override
+    public boolean hasNext() {
+        //From the perspective of JsonParser not finished until END_OBJECT is being read.
+        return state != State.END;
+    }
+
+    /**
+     * {@link JsonValue} for current key.
+     * @return Current JsonValue.
+     */
+    public JsonValue getValue() {
+        return jsonObject.get(currentKey);
+    }
+
+    @Override
+    String getString() {
+        if (state == JsonObjectIterator.State.KEY) {
+            return currentKey;
+        }
+        return super.getString();
+    }
+
+    @Override
+    JsonbException createIncompatibleValueError() {
+        return new JsonbException(Messages.getMessage(MessageKeys.NUMBER_INCOMPATIBLE_VALUE_TYPE_OBJECT, getValue().getValueType(), currentKey));
+    }
+
+    private void setState(State state) {
+        this.state = state;
+    }
+
+    /**
+     * Current key this iterator is pointing at.
+     * @return Current key.
+     */
+    public String getKey() {
+        return currentKey;
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureBuilder.java b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureBuilder.java
new file mode 100644
index 0000000..38c0bd4
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureBuilder.java
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
+ * which accompanies this distribution.
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ ******************************************************************************/
+package org.eclipse.yasson.internal.jsonstructure;
+
+import javax.json.JsonStructure;
+import javax.json.JsonValue;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * Grouping interface for {@link javax.json.JsonObject} and {@link javax.json.JsonArray} generation.
+ */
+abstract class JsonStructureBuilder {
+
+    /**
+     * Build and get constructed {@link JsonStructure}
+     * @return JsonStructure result.
+     */
+    abstract JsonStructure build();
+
+    /**
+     * Puts another {@link JsonStructure} into current. If current is {@link javax.json.JsonObject} than last written
+     * key is used.
+     * @param structure
+     */
+    abstract void put(JsonStructure structure);
+
+    /**
+     * Write a value into current {@link JsonStructure}. If current is {@link javax.json.JsonObject}, last stored key
+     * by {@link JsonObjectBuilder#writeKey(String)} is used.
+     *
+     * @param value A value to write.
+     */
+    abstract void write(JsonValue value);
+
+    /**
+     * Write a value into current {@link JsonStructure}. If current is {@link javax.json.JsonObject}, last stored key
+     * by {@link JsonObjectBuilder#writeKey(String)} is used.
+     *
+     * @param value A value to write.
+     */
+    abstract void write(String value);
+
+    /**
+     * Write a value into current {@link JsonStructure}. If current is {@link javax.json.JsonObject}, last stored key
+     * by {@link JsonObjectBuilder#writeKey(String)} is used.
+     *
+     * @param value A value to write.
+     */
+    abstract void write(BigDecimal value);
+
+    /**
+     * Write a value into current {@link JsonStructure}. If current is {@link javax.json.JsonObject}, last stored key
+     * by {@link JsonObjectBuilder#writeKey(String)} is used.
+     *
+     * @param value A value to write.
+     */
+    abstract void write(BigInteger value);
+
+    /**
+     * Write a value into current {@link JsonStructure}. If current is {@link javax.json.JsonObject}, last stored key
+     * by {@link JsonObjectBuilder#writeKey(String)} is used.
+     *
+     * @param value A value to write.
+     */
+    abstract void write(int value);
+
+    /**
+     * Write a value into current {@link JsonStructure}. If current is {@link javax.json.JsonObject}, last stored key
+     * by {@link JsonObjectBuilder#writeKey(String)} is used.
+     *
+     * @param value A value to write.
+     */
+    abstract void write(long value);
+
+    /**
+     * Write a value into current {@link JsonStructure}. If current is {@link javax.json.JsonObject}, last stored key
+     * by {@link JsonObjectBuilder#writeKey(String)} is used.
+     *
+     * @param value A value to write.
+     */
+    abstract void write(double value);
+
+    /**
+     * Write a value into current {@link JsonStructure}. If current is {@link javax.json.JsonObject}, last stored key
+     * by {@link JsonObjectBuilder#writeKey(String)} is used.
+     *
+     * @param value A value to write.
+     */
+    abstract void write(boolean value);
+
+    /**
+     * Write null into current {@link JsonStructure}. If current is {@link javax.json.JsonObject}, last stored key
+     * by {@link JsonObjectBuilder#writeKey(String)} is used.
+     */
+    abstract void writeNull();
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureIterator.java b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureIterator.java
new file mode 100644
index 0000000..8ea3578
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureIterator.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
+ * which accompanies this distribution.
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ ******************************************************************************/
+package org.eclipse.yasson.internal.jsonstructure;
+
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+import javax.json.JsonString;
+import javax.json.JsonValue;
+import javax.json.bind.JsonbException;
+import javax.json.stream.JsonParser;
+import java.util.Iterator;
+
+/**
+ * Iterates over {@link javax.json.JsonStructure}.
+ */
+abstract class JsonStructureIterator implements Iterator<JsonParser.Event> {
+
+    /**
+     * Get current {@link JsonValue}, that the parser is pointing on.
+     * @return JsonValue result.
+     */
+    abstract JsonValue getValue();
+
+    /**
+     * Creates an exception for throwing in case of current value type is not compatible with
+     * called getter return type.
+     *
+     * @return JsonbException with error description.
+     */
+    abstract JsonbException createIncompatibleValueError();
+
+    /**
+     * Check the type of current  {@link JsonValue} and return a string representing a value.
+     * @return String value for current JsonValue
+     */
+    String getString() {
+        JsonValue value = getValue();
+        if (value instanceof JsonString) {
+            return ((JsonString) value).getString();
+        } else {
+            return value.toString();
+        }
+    }
+
+    /**
+     * Convert {@link JsonValue} type to {@link JsonParser.Event}.
+     * @param value JsonValue
+     * @return JsonParser event
+     */
+    JsonParser.Event getValueEvent(JsonValue value) {
+        switch (value.getValueType()) {
+            case NUMBER:
+                return JsonParser.Event.VALUE_NUMBER;
+            case STRING:
+            case TRUE:
+            case FALSE:
+                return JsonParser.Event.VALUE_STRING;
+            case OBJECT:
+                return JsonParser.Event.START_OBJECT;
+            case ARRAY:
+                return JsonParser.Event.START_ARRAY;
+            case NULL:
+                return JsonParser.Event.VALUE_NULL;
+                default:
+                    throw new JsonbException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "unknown json value: " + value.getValueType()));
+        }
+
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureToParserAdapter.java b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureToParserAdapter.java
new file mode 100644
index 0000000..50c4592
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureToParserAdapter.java
@@ -0,0 +1,114 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
+ * which accompanies this distribution.
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ ******************************************************************************/
+package org.eclipse.yasson.internal.jsonstructure;
+
+import javax.json.JsonArray;
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import javax.json.JsonStructure;
+import javax.json.JsonValue;
+import javax.json.bind.JsonbException;
+import javax.json.stream.JsonLocation;
+import javax.json.stream.JsonParser;
+import java.math.BigDecimal;
+import java.util.Stack;
+
+/**
+ * Adapter for {@link JsonParser}, that reads a {@link JsonStructure} content tree instead of JSON text.
+ *
+ * Yasson and jsonb API components are using {@link JsonParser} as its input API.
+ * This adapter allows deserialization of {@link JsonStructure} into java content tree using same components
+ * as when parsing JSON text.
+ */
+public class JsonStructureToParserAdapter implements JsonParser {
+
+    private Stack<JsonStructureIterator> iterators = new Stack<>();
+
+    private final JsonStructure rootStructure;
+
+    public JsonStructureToParserAdapter(JsonStructure structure) {
+        this.rootStructure = structure;
+    }
+
+    @Override
+    public boolean hasNext() {
+        return iterators.peek().hasNext();
+    }
+
+    @Override
+    public Event next() {
+        if (iterators.isEmpty()) {
+            if (rootStructure instanceof JsonObject) {
+                iterators.push(new JsonObjectIterator((JsonObject) rootStructure));
+                return Event.START_OBJECT;
+            } else if (rootStructure instanceof JsonArray) {
+                iterators.push(new JsonArrayIterator((JsonArray) rootStructure));
+                return Event.START_ARRAY;
+            }
+        }
+        JsonStructureIterator current = iterators.peek();
+        Event next = current.next();
+        if (next == Event.START_OBJECT) {
+            iterators.push(new JsonObjectIterator((JsonObject) iterators.peek().getValue()));
+        } else if (next == Event.START_ARRAY) {
+            iterators.push(new JsonArrayIterator((JsonArray) iterators.peek().getValue()));
+        } else if (next == Event.END_OBJECT || next == Event.END_ARRAY) {
+            iterators.pop();
+        }
+        return next;
+    }
+
+
+
+    @Override
+    public String getString() {
+        return iterators.peek().getString();
+    }
+
+    @Override
+    public boolean isIntegralNumber() {
+        return getJsonNumberValue().isIntegral();
+    }
+
+    @Override
+    public int getInt() {
+        return getJsonNumberValue().intValueExact();
+    }
+
+    @Override
+    public long getLong() {
+        return getJsonNumberValue().longValueExact();
+    }
+
+    @Override
+    public BigDecimal getBigDecimal() {
+        return getJsonNumberValue().bigDecimalValue();
+    }
+
+    private JsonNumber getJsonNumberValue() {
+        JsonStructureIterator iterator = iterators.peek();
+        JsonValue value = iterator.getValue();
+        if (value.getValueType() != JsonValue.ValueType.NUMBER) {
+            throw iterator.createIncompatibleValueError();
+        }
+        return (JsonNumber) value;
+    }
+
+    @Override
+    public JsonLocation getLocation() {
+        throw new JsonbException("Operation not supported");
+    }
+
+    @Override
+    public void close() {
+        //noop
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/properties/MessageKeys.java b/src/main/java/org/eclipse/yasson/internal/properties/MessageKeys.java
index 475f8dd..3336dda 100644
--- a/src/main/java/org/eclipse/yasson/internal/properties/MessageKeys.java
+++ b/src/main/java/org/eclipse/yasson/internal/properties/MessageKeys.java
@@ -86,6 +86,8 @@
     MULTIPLE_CONSTRUCTOR_PROPERTIES_CREATORS("multipleConstructorPropertiesCreators"),
     ANNOTATION_NOT_AVAILABLE("annotationNotAvailable"),
     MISSING_VALUE_PROPERTY_IN_ANNOTATION("missingValuePropertyInAnnotation"),
+    NUMBER_INCOMPATIBLE_VALUE_TYPE_ARRAY("numberIncompatibleValueTypeArray"),
+    NUMBER_INCOMPATIBLE_VALUE_TYPE_OBJECT("numberIncompatibleValueTypeObject"),
     ;
 
     /** Message bundle key. */
diff --git a/src/main/resources/yasson-messages.properties b/src/main/resources/yasson-messages.properties
index 5ed7eff..59b8a68 100644
--- a/src/main/resources/yasson-messages.properties
+++ b/src/main/resources/yasson-messages.properties
@@ -79,4 +79,6 @@
 datatypeFactoryCreationFailed=An error occurred while DatatypeFactory creation.
 multipleConstructorPropertiesCreators=More than one constructor annotated with @ConstructorProperties declared in class {0}.
 annotationNotAvailable=Annotation {0} is not visible in modules or classpath. Annotation will be ignored.
-missingValuePropertyInAnnotation=Missing value property in Annotation {0}. Annotation will be ignored.
\ No newline at end of file
+missingValuePropertyInAnnotation=Missing value property in Annotation {0}. Annotation will be ignored.
+numberIncompatibleValueTypeArray=Value type {0} is not a JsonNumber.
+numberIncompatibleValueTypeObject=Value type {0} at key {1} is not a JsonNumber.
\ No newline at end of file
diff --git a/src/test/java/org/eclipse/yasson/jsonstructure/InnerPojo.java b/src/test/java/org/eclipse/yasson/jsonstructure/InnerPojo.java
new file mode 100644
index 0000000..0a0faa6
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/jsonstructure/InnerPojo.java
@@ -0,0 +1,22 @@
+package org.eclipse.yasson.jsonstructure;
+
+public final class InnerPojo {
+    private String innerFirst;
+    private String innerSecond;
+
+    public String getInnerFirst() {
+        return innerFirst;
+    }
+
+    public void setInnerFirst(String innerFirst) {
+        this.innerFirst = innerFirst;
+    }
+
+    public String getInnerSecond() {
+        return innerSecond;
+    }
+
+    public void setInnerSecond(String innerSecond) {
+        this.innerSecond = innerSecond;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/jsonstructure/InnerPojoDeserializer.java b/src/test/java/org/eclipse/yasson/jsonstructure/InnerPojoDeserializer.java
new file mode 100644
index 0000000..dee7b03
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/jsonstructure/InnerPojoDeserializer.java
@@ -0,0 +1,26 @@
+package org.eclipse.yasson.jsonstructure;
+
+import javax.json.bind.serializer.DeserializationContext;
+import javax.json.bind.serializer.JsonbDeserializer;
+import javax.json.stream.JsonParser;
+import java.lang.reflect.Type;
+
+public class InnerPojoDeserializer implements JsonbDeserializer<InnerPojo> {
+    @Override
+    public InnerPojo deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) {
+        InnerPojo innerPojo = new InnerPojo();
+        //KEY first
+        parser.next();
+        //VALUE
+        parser.next();
+        innerPojo.setInnerFirst(parser.getString());
+        //KEY second
+        parser.next();
+        //VALUE
+        parser.next();
+        innerPojo.setInnerSecond(parser.getString());
+        //END_OBJECT
+        parser.next();
+        return innerPojo;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/jsonstructure/InnerPojoSerializer.java b/src/test/java/org/eclipse/yasson/jsonstructure/InnerPojoSerializer.java
new file mode 100644
index 0000000..3c21dd4
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/jsonstructure/InnerPojoSerializer.java
@@ -0,0 +1,16 @@
+package org.eclipse.yasson.jsonstructure;
+
+import javax.json.bind.serializer.JsonbSerializer;
+import javax.json.bind.serializer.SerializationContext;
+import javax.json.stream.JsonGenerator;
+
+public class InnerPojoSerializer implements JsonbSerializer<InnerPojo> {
+
+    @Override
+    public void serialize(InnerPojo obj, JsonGenerator generator, SerializationContext ctx) {
+        generator.writeStartObject();
+        generator.write("first", obj.getInnerFirst());
+        generator.write("second", obj.getInnerSecond());
+        generator.writeEnd();
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/jsonstructure/JsonGeneratorToStructureAdapterTest.java b/src/test/java/org/eclipse/yasson/jsonstructure/JsonGeneratorToStructureAdapterTest.java
new file mode 100644
index 0000000..2fc0c18
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/jsonstructure/JsonGeneratorToStructureAdapterTest.java
@@ -0,0 +1,150 @@
+package org.eclipse.yasson.jsonstructure;
+
+import org.eclipse.yasson.YassonJsonb;
+import org.junit.Test;
+
+import javax.json.JsonArray;
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import javax.json.JsonString;
+import javax.json.JsonStructure;
+import javax.json.JsonValue;
+import javax.json.bind.JsonbBuilder;
+import javax.json.bind.JsonbConfig;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class JsonGeneratorToStructureAdapterTest {
+
+    private final YassonJsonb jsonb = (YassonJsonb) JsonbBuilder.create();
+
+    @Test
+    public void testSimplePojo() {
+        Pojo pojo = new Pojo();
+        pojo.setBigDecimalProperty(BigDecimal.TEN);
+        pojo.setLongProperty(10L);
+        pojo.setStringProperty("String value");
+
+        JsonObject result = (JsonObject) jsonb.toJsonStructure(pojo);
+
+        assertEquals("String value", getString(result.get("stringProperty")));
+        JsonValue bigDecimalProperty = result.get("bigDecimalProperty");
+        assertEquals(JsonValue.ValueType.NUMBER, bigDecimalProperty.getValueType());
+        assertEquals(BigDecimal.TEN, ((JsonNumber) bigDecimalProperty).bigDecimalValue());
+        JsonValue longProperty = result.get("longProperty");
+        assertEquals(JsonValue.ValueType.NUMBER, longProperty.getValueType());
+        assertEquals(10L, ((JsonNumber) longProperty).longValueExact());
+    }
+
+    @Test
+    public void testInnerObjects() {
+        Pojo pojo = new Pojo();
+        pojo.setBigDecimalProperty(BigDecimal.TEN);
+        pojo.setLongProperty(10L);
+        pojo.setStringProperty("String value");
+        pojo.setInner(new InnerPojo());
+        pojo.getInner().setInnerFirst("First");
+        pojo.getInner().setInnerSecond("Second");
+
+        JsonObject result = (JsonObject) jsonb.toJsonStructure(pojo, Pojo.class);
+        assertEquals("String value", getString(result.get("stringProperty")));
+        JsonValue bigDecimalProperty = result.get("bigDecimalProperty");
+        assertTrue(bigDecimalProperty instanceof JsonNumber);
+        assertEquals(BigDecimal.TEN, ((JsonNumber) bigDecimalProperty).bigDecimalValue());
+        JsonValue longProperty = result.get("longProperty");
+        assertTrue(longProperty instanceof JsonNumber);
+        assertEquals(10L, ((JsonNumber) longProperty).longValueExact());
+
+        JsonValue inner = result.get("inner");
+        assertEquals(JsonValue.ValueType.OBJECT, inner.getValueType());
+        assertEquals("First", ((JsonObject)inner).getString("innerFirst"));
+        assertEquals("Second", ((JsonObject)inner).getString("innerSecond"));
+
+    }
+
+    @Test
+    public void testSimpleJsonArray() {
+        List<Object> objList = new ArrayList<>();
+        objList.add("First");
+        objList.add(10L);
+        objList.add(BigDecimal.ONE);
+        objList.add(Boolean.TRUE);
+        objList.add(null);
+
+        JsonArray result = (JsonArray) jsonb.toJsonStructure(objList);
+        assertEquals("First", result.getString(0));
+        assertEquals(10L, result.getJsonNumber(1).longValueExact());
+        assertEquals(BigDecimal.ONE, result.getJsonNumber(2).bigDecimalValue());
+        assertEquals(Boolean.TRUE, result.getBoolean(3));
+        assertEquals(JsonValue.ValueType.NULL, result.get(4).getValueType());
+    }
+
+    @Test
+    public void testJsonArrayInJsonObject() {
+        Pojo pojo = new Pojo();
+        pojo.setStrings(new ArrayList<>());
+        pojo.setBigDecimals(new ArrayList<>());
+        pojo.setBooleans(new ArrayList<>());
+        pojo.getStrings().add("First");
+        pojo.getBigDecimals().add(BigDecimal.TEN);
+        pojo.getBooleans().add(Boolean.TRUE);
+
+        JsonObject result = (JsonObject) jsonb.toJsonStructure(pojo);
+        assertEquals(JsonValue.ValueType.ARRAY, result.get("strings").getValueType());
+        assertEquals(JsonValue.ValueType.ARRAY, result.get("bigDecimals").getValueType());
+        assertEquals(JsonValue.ValueType.ARRAY, result.get("booleans").getValueType());
+        assertEquals("First", result.getJsonArray("strings").getString(0));
+        assertEquals(BigDecimal.TEN, result.getJsonArray("bigDecimals").getJsonNumber(0).bigDecimalValue());
+        assertEquals(Boolean.TRUE, result.getJsonArray("booleans").getBoolean(0));
+    }
+
+    @Test
+    public void testNestedJsonArrays() {
+        List<List<Object>> outer = new ArrayList<>();
+        List<Object> inner = new ArrayList<>();
+        inner.add("First");
+        inner.add(10L);
+        inner.add(BigDecimal.ONE);
+        inner.add(Boolean.TRUE);
+        inner.add(null);
+        outer.add(inner);
+
+        JsonArray result = (JsonArray) jsonb.toJsonStructure(outer);
+        assertEquals(JsonValue.ValueType.ARRAY, result.get(0).getValueType());
+        JsonArray resultInner = result.getJsonArray(0);
+
+        assertEquals("First", resultInner.getString(0));
+        assertEquals(10L, resultInner.getJsonNumber(1).longValueExact());
+        assertEquals(BigDecimal.ONE, resultInner.getJsonNumber(2).bigDecimalValue());
+        assertEquals(Boolean.TRUE, resultInner.getBoolean(3));
+        assertEquals(JsonValue.ValueType.NULL, resultInner.get(4).getValueType());
+    }
+
+    @Test
+    public void testCustomJsonbSerializer() {
+        Pojo pojo = new Pojo();
+        pojo.setInner(new InnerPojo());
+        pojo.getInner().setInnerFirst("First value");
+        pojo.getInner().setInnerSecond("Second value");
+        YassonJsonb jsonb = (YassonJsonb) JsonbBuilder.create(new JsonbConfig().withSerializers(new InnerPojoSerializer()));
+        JsonStructure result = jsonb.toJsonStructure(pojo);
+        assertEquals(JsonValue.ValueType.OBJECT, result.getValueType());
+        assertEquals(JsonValue.ValueType.OBJECT, ((JsonObject) result).get("inner").getValueType());
+        JsonObject inner = (JsonObject) ((JsonObject) result).get("inner");
+        assertEquals(JsonValue.ValueType.STRING, inner.get("first").getValueType());
+        assertEquals("First value", ((JsonString) inner.get("first")).getString());
+        assertEquals(JsonValue.ValueType.STRING, inner.get("second").getValueType());
+        assertEquals("Second value", ((JsonString) inner.get("second")).getString());
+    }
+
+    private String getString(JsonValue value) {
+        if (value instanceof JsonString) {
+            return ((JsonString) value).getString();
+        }
+        return value.toString();
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/jsonstructure/JsonStructureToParserAdapterTest.java b/src/test/java/org/eclipse/yasson/jsonstructure/JsonStructureToParserAdapterTest.java
new file mode 100644
index 0000000..44e5327
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/jsonstructure/JsonStructureToParserAdapterTest.java
@@ -0,0 +1,266 @@
+package org.eclipse.yasson.jsonstructure;
+
+import org.eclipse.yasson.TestTypeToken;
+import org.eclipse.yasson.YassonJsonb;
+import org.junit.Test;
+
+import javax.json.JsonArray;
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+import javax.json.bind.JsonbBuilder;
+import javax.json.bind.JsonbConfig;
+import javax.json.spi.JsonProvider;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+public class JsonStructureToParserAdapterTest {
+
+    private final YassonJsonb jsonb = (YassonJsonb) JsonbBuilder.create();
+
+    private final JsonProvider jsonProvider = JsonProvider.provider();
+
+    @Test
+    public void testBasicJsonObject() {
+        JsonObjectBuilder objectBuilder = jsonProvider.createObjectBuilder();
+        objectBuilder.add("stringProperty", "value 1");
+        objectBuilder.add("bigDecimalProperty", new BigDecimal("1.1"));
+        objectBuilder.add("longProperty", 10L);
+        JsonObject jsonObject = objectBuilder.build();
+        Pojo result = jsonb.fromJsonStructure(jsonObject, Pojo.class);
+        assertEquals("value 1", result.getStringProperty());
+        assertEquals(new BigDecimal("1.1"), result.getBigDecimalProperty());
+        assertEquals(Long.valueOf(10), result.getLongProperty());
+    }
+
+    @Test
+    public void testNullValues() {
+        JsonObjectBuilder objectBuilder = jsonProvider.createObjectBuilder();
+        objectBuilder.addNull("stringProperty");
+        objectBuilder.addNull("bigDecimalProperty");
+        objectBuilder.add("longProperty", 10L);
+        JsonObject jsonObject = objectBuilder.build();
+        Pojo result = jsonb.fromJsonStructure(jsonObject, Pojo.class);
+        assertNull(result.getStringProperty());
+        assertNull(result.getBigDecimalProperty());
+        assertEquals(Long.valueOf(10), result.getLongProperty());
+    }
+
+    @Test
+    public void testInnerJsonObjectWrappedWithProperties() {
+        JsonObjectBuilder innerBuilder = jsonProvider.createObjectBuilder();
+        innerBuilder.add("innerFirst", "Inner value 1");
+        innerBuilder.add("innerSecond", "Inner value 2");
+
+        JsonObjectBuilder objectBuilder = jsonProvider.createObjectBuilder();
+
+        objectBuilder.add("stringProperty", "value 1");
+        objectBuilder.add("inner", innerBuilder.build());
+        objectBuilder.add("bigDecimalProperty", new BigDecimal("1.1"));
+        objectBuilder.add("longProperty", 10L);
+        JsonObject jsonObject = objectBuilder.build();
+        Pojo result = jsonb.fromJsonStructure(jsonObject, Pojo.class);
+
+        assertEquals("value 1", result.getStringProperty());
+        assertEquals(new BigDecimal("1.1"), result.getBigDecimalProperty());
+        assertEquals(Long.valueOf(10), result.getLongProperty());
+        assertEquals("Inner value 1", result.getInner().getInnerFirst());
+        assertEquals("Inner value 2", result.getInner().getInnerSecond());
+    }
+
+    @Test
+    public void testInnerJsonObjectAtEndProperty() {
+        JsonObjectBuilder innerBuilder = jsonProvider.createObjectBuilder();
+        innerBuilder.add("innerFirst", "Inner value 1");
+        innerBuilder.add("innerSecond", "Inner value 2");
+
+        JsonObjectBuilder objectBuilder = jsonProvider.createObjectBuilder();
+
+        objectBuilder.add("stringProperty", "value 1");
+        objectBuilder.add("bigDecimalProperty", new BigDecimal("1.1"));
+        objectBuilder.add("longProperty", 10L);
+        objectBuilder.add("inner", innerBuilder.build());
+
+        JsonObject jsonObject = objectBuilder.build();
+        Pojo result = jsonb.fromJsonStructure(jsonObject, Pojo.class);
+
+        assertEquals("value 1", result.getStringProperty());
+        assertEquals(new BigDecimal("1.1"), result.getBigDecimalProperty());
+        assertEquals(Long.valueOf(10), result.getLongProperty());
+        assertEquals("Inner value 1", result.getInner().getInnerFirst());
+        assertEquals("Inner value 2", result.getInner().getInnerSecond());
+
+    }
+
+    @Test
+    public void testEmptyJsonObject() {
+        JsonObjectBuilder objectBuilder = jsonProvider.createObjectBuilder();
+        JsonObject jsonObject = objectBuilder.build();
+        Pojo result = jsonb.fromJsonStructure(jsonObject, Pojo.class);
+        assertNull(result.getStringProperty());
+        assertNull(result.getBigDecimalProperty());
+        assertNull(result.getLongProperty());
+    }
+
+    @Test
+    public void testEmptyInnerJsonObject() {
+        JsonObjectBuilder objectBuilder = jsonProvider.createObjectBuilder();
+
+        JsonObjectBuilder innerBuilder = jsonProvider.createObjectBuilder();
+        JsonObject innerObject = innerBuilder.build();
+
+        objectBuilder.add("inner", innerObject);
+
+        JsonObject jsonObject = objectBuilder.build();
+
+        Pojo result = jsonb.fromJsonStructure(jsonObject, Pojo.class);
+        assertNull(result.getStringProperty());
+        assertNull(result.getBigDecimalProperty());
+        assertNull(result.getLongProperty());
+
+        assertNotNull(result.getInner());
+        assertNull(result.getInner().getInnerFirst());
+        assertNull(result.getInner().getInnerSecond());
+    }
+
+    @Test
+    public void testSimpleArray() {
+        JsonArrayBuilder arrayBuilder = jsonProvider.createArrayBuilder();
+        arrayBuilder.add(BigDecimal.TEN).add("String value").addNull();
+        JsonArray jsonArray = arrayBuilder.build();
+        List result = jsonb.fromJsonStructure(jsonArray, ArrayList.class);
+        assertEquals(3, result.size());
+        assertEquals(BigDecimal.TEN, result.get(0));
+        assertEquals("String value", result.get(1));
+        assertNull(result.get(2));
+    }
+
+    @Test
+    public void testArraysInsideObject() {
+        JsonArrayBuilder bigDecBuilder = jsonProvider.createArrayBuilder();
+        JsonArrayBuilder strBuilder = jsonProvider.createArrayBuilder();
+        JsonArrayBuilder blnBuilder = jsonProvider.createArrayBuilder();
+
+        bigDecBuilder.add(BigDecimal.TEN);
+        strBuilder.add("String value 1");
+        blnBuilder.add(Boolean.TRUE);
+
+        JsonObjectBuilder pojoBuilder = jsonProvider.createObjectBuilder();
+        pojoBuilder.add("strings", strBuilder.build());
+        pojoBuilder.add("bigDecimals", bigDecBuilder.build());
+        pojoBuilder.add("booleans", blnBuilder.build());
+
+        JsonObject jsonObject = pojoBuilder.build();
+        Pojo pojo = jsonb.fromJsonStructure(jsonObject, Pojo.class);
+
+        assertEquals(1, pojo.getBigDecimals().size());
+        assertEquals(1, pojo.getStrings().size());
+        assertEquals(1, pojo.getBooleans().size());
+    }
+
+    @Test
+    public void testNestedArrays() {
+        JsonArrayBuilder arrayBuilder = jsonProvider.createArrayBuilder();
+        JsonArrayBuilder innerArrBuilder = jsonProvider.createArrayBuilder();
+        innerArrBuilder.add("first").add("second");
+        arrayBuilder.add(BigDecimal.TEN);
+        arrayBuilder.add(innerArrBuilder.build());
+
+        JsonArray jsonArray = arrayBuilder.build();
+
+        ArrayList result = jsonb.fromJsonStructure(jsonArray, ArrayList.class);
+        assertEquals(2, result.size());
+        assertEquals(BigDecimal.TEN, result.get(0));
+        assertTrue(result.get(1) instanceof List);
+        List inner = (List) result.get(1);
+        assertEquals(2, inner.size());
+        assertEquals("first", inner.get(0));
+        assertEquals("second", inner.get(1));
+    }
+
+    @Test
+    public void testObjectsNestedInArrays() {
+        JsonObjectBuilder objectBuilder = jsonProvider.createObjectBuilder();
+        objectBuilder.add("stringProperty", "value 1");
+        objectBuilder.add("bigDecimalProperty", new BigDecimal("1.1"));
+        objectBuilder.add("longProperty", 10L);
+
+        JsonArrayBuilder innerArrayBuilder = jsonProvider.createArrayBuilder();
+        innerArrayBuilder.add("String value 1");
+        objectBuilder.add("strings", innerArrayBuilder.build());
+
+        JsonArrayBuilder arrayBuilder = jsonProvider.createArrayBuilder();
+        arrayBuilder.add(objectBuilder.build());
+
+        JsonArray rootArray = arrayBuilder.build();
+
+        List<Object> result = jsonb.fromJsonStructure(rootArray, new TestTypeToken<List<Pojo>>(){}.getType());
+        assertTrue(result.get(0) instanceof Pojo);
+        Pojo pojo = (Pojo) result.get(0);
+        assertNotNull(pojo);
+        assertEquals("value 1", pojo.getStringProperty());
+        assertEquals(new BigDecimal("1.1"), pojo.getBigDecimalProperty());
+        assertEquals(Long.valueOf(10), pojo.getLongProperty());
+        assertNotNull(pojo.getStrings());
+        assertEquals(1, pojo.getStrings().size());
+        assertEquals("String value 1", pojo.getStrings().get(0));
+    }
+
+    @Test
+    public void testObjectsNestedInArraysRaw() {
+        JsonObjectBuilder objectBuilder = jsonProvider.createObjectBuilder();
+        objectBuilder.add("stringProperty", "value 1");
+        objectBuilder.add("bigDecimalProperty", new BigDecimal("1.1"));
+        objectBuilder.add("longProperty", 10L);
+
+        JsonArrayBuilder innerArrayBuilder = jsonProvider.createArrayBuilder();
+        innerArrayBuilder.add("String value 1");
+
+        objectBuilder.add("strings", innerArrayBuilder.build());
+
+        JsonArrayBuilder arrayBuilder = jsonProvider.createArrayBuilder();
+        arrayBuilder.add(10L);
+        arrayBuilder.add(objectBuilder.build());
+        arrayBuilder.add("10");
+
+        JsonArray rootArray = arrayBuilder.build();
+
+        List<Object> result = jsonb.fromJsonStructure(rootArray, new TestTypeToken<List<Object>>(){}.getType());
+        assertEquals(new BigDecimal("10"), result.get(0));
+        assertTrue(result.get(1) instanceof Map);
+        Map pojo = (Map) result.get(1);
+        assertNotNull(pojo);
+        assertEquals("value 1", pojo.get("stringProperty"));
+        assertEquals(new BigDecimal("1.1"), pojo.get("bigDecimalProperty"));
+        assertEquals(new BigDecimal(10), pojo.get("longProperty"));
+        assertTrue(pojo.get("strings") instanceof List);
+        List strings = (List) pojo.get("strings");
+        assertNotNull(strings);
+        assertEquals(1, strings.size());
+        assertEquals("String value 1", strings.get(0));
+    }
+
+
+    @Test
+    public void testCustomJsonbDeserializer() {
+        JsonObjectBuilder outerBuilder = jsonProvider.createObjectBuilder();
+        JsonObjectBuilder innerBuilder = jsonProvider.createObjectBuilder();
+        innerBuilder.add("first", "String value 1");
+        innerBuilder.add("second", "String value 2");
+        outerBuilder.add("inner", innerBuilder.build());
+        JsonObject object = outerBuilder.build();
+
+        YassonJsonb jsonb = (YassonJsonb) JsonbBuilder.create(new JsonbConfig().withDeserializers(new InnerPojoDeserializer()));
+        Pojo result = jsonb.fromJsonStructure(object, Pojo.class);
+        assertNotNull(result.getInner());
+        assertEquals("String value 1", result.getInner().getInnerFirst());
+        assertEquals("String value 2", result.getInner().getInnerSecond());
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/jsonstructure/Pojo.java b/src/test/java/org/eclipse/yasson/jsonstructure/Pojo.java
new file mode 100644
index 0000000..f5e2e7d
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/jsonstructure/Pojo.java
@@ -0,0 +1,72 @@
+package org.eclipse.yasson.jsonstructure;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+public final class Pojo {
+
+    private String stringProperty;
+    private InnerPojo inner;
+    private BigDecimal bigDecimalProperty;
+    private Long longProperty;
+
+    private List<String> strings;
+    private List<BigDecimal> bigDecimals;
+    private List<Boolean> booleans;
+
+    public String getStringProperty() {
+        return stringProperty;
+    }
+
+    public void setStringProperty(String stringProperty) {
+        this.stringProperty = stringProperty;
+    }
+
+    public BigDecimal getBigDecimalProperty() {
+        return bigDecimalProperty;
+    }
+
+    public void setBigDecimalProperty(BigDecimal bigDecimalProperty) {
+        this.bigDecimalProperty = bigDecimalProperty;
+    }
+
+    public InnerPojo getInner() {
+        return inner;
+    }
+
+    public void setInner(InnerPojo inner) {
+        this.inner = inner;
+    }
+
+    public Long getLongProperty() {
+        return longProperty;
+    }
+
+    public void setLongProperty(Long longProperty) {
+        this.longProperty = longProperty;
+    }
+
+    public List<String> getStrings() {
+        return strings;
+    }
+
+    public void setStrings(List<String> strings) {
+        this.strings = strings;
+    }
+
+    public List<BigDecimal> getBigDecimals() {
+        return bigDecimals;
+    }
+
+    public void setBigDecimals(List<BigDecimal> bigDecimals) {
+        this.bigDecimals = bigDecimals;
+    }
+
+    public List<Boolean> getBooleans() {
+        return booleans;
+    }
+
+    public void setBooleans(List<Boolean> booleans) {
+        this.booleans = booleans;
+    }
+}