You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by pa...@apache.org on 2017/11/24 22:11:07 UTC

[sling-org-apache-sling-commons-johnzon] branch SLING-7267 created (now 62ab59b)

This is an automated email from the ASF dual-hosted git repository.

pauls pushed a change to branch SLING-7267
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-johnzon.git.


      at 62ab59b  SLING-7267: Update commons johnzon to johnzon 1.1.4 and make it work with java7.

This branch includes the following new commits:

     new 62ab59b  SLING-7267: Update commons johnzon to johnzon 1.1.4 and make it work with java7.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


-- 
To stop receiving notification emails like this one, please contact
['"commits@sling.apache.org" <co...@sling.apache.org>'].

[sling-org-apache-sling-commons-johnzon] 01/01: SLING-7267: Update commons johnzon to johnzon 1.1.4 and make it work with java7.

Posted by pa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pauls pushed a commit to branch SLING-7267
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-johnzon.git

commit 62ab59b4b2af5ea9989943a67440a75898c0e9ee
Author: Karl Pauls <ka...@gmail.com>
AuthorDate: Fri Nov 24 23:10:46 2017 +0100

    SLING-7267: Update commons johnzon to johnzon 1.1.4 and make it work with java7.
---
 pom.xml                                            |   37 +-
 src/main/java/java/util/function/Function.java     |   23 +
 src/main/java/java/util/stream/Stream.java         |   22 +
 src/main/java/javax/json/Json.java                 |  359 +++++++
 src/main/java/javax/json/JsonArray.java            |  153 +++
 src/main/java/javax/json/JsonArrayBuilder.java     |   94 ++
 src/main/java/javax/json/JsonBuilderFactory.java   |   81 ++
 src/main/java/javax/json/JsonException.java        |   27 +
 src/main/java/javax/json/JsonMergePatch.java       |   40 +
 src/main/java/javax/json/JsonNumber.java           |   61 ++
 src/main/java/javax/json/JsonObject.java           |  103 ++
 src/main/java/javax/json/JsonObjectBuilder.java    |  148 +++
 src/main/java/javax/json/JsonPatch.java            |  208 ++++
 src/main/java/javax/json/JsonPatchBuilder.java     |  193 ++++
 src/main/java/javax/json/JsonPointer.java          |   94 ++
 src/main/java/javax/json/JsonReader.java           |   32 +
 src/main/java/javax/json/JsonReaderFactory.java    |   32 +
 src/main/java/javax/json/JsonString.java           |   32 +
 src/main/java/javax/json/JsonStructure.java        |   28 +
 src/main/java/javax/json/JsonValue.java            |  149 +++
 src/main/java/javax/json/JsonWriter.java           |   39 +
 src/main/java/javax/json/JsonWriterFactory.java    |   32 +
 src/main/java/javax/json/spi/JsonProvider.java     |  105 +-
 .../javax/json/stream/JsonGenerationException.java |   32 +
 src/main/java/javax/json/stream/JsonGenerator.java |   88 ++
 .../javax/json/stream/JsonGeneratorFactory.java    |   32 +
 src/main/java/javax/json/stream/JsonLocation.java  |   26 +
 src/main/java/javax/json/stream/JsonParser.java    |   71 ++
 .../java/javax/json/stream/JsonParserFactory.java  |   39 +
 .../javax/json/stream/JsonParsingException.java    |   39 +
 .../apache/johnzon/core/AbstractJsonFactory.java   |   84 ++
 .../org/apache/johnzon/core/BufferStrategy.java    |  272 +++++
 .../johnzon/core/CommentsJsonStreamParserImpl.java |   73 ++
 .../java/org/apache/johnzon/core/DiffBase.java     |   34 +
 .../java/org/apache/johnzon/core/Experimental.java |   33 +
 src/main/java/org/apache/johnzon/core/HStack.java  |   66 ++
 .../org/apache/johnzon/core/JohnzonJsonParser.java |  134 +++
 .../apache/johnzon/core/JsonArrayBuilderImpl.java  |  330 ++++++
 .../org/apache/johnzon/core/JsonArrayImpl.java     |  235 +++++
 .../johnzon/core/JsonBuilderFactoryImpl.java       |   92 ++
 .../java/org/apache/johnzon/core/JsonChars.java    |   82 ++
 .../org/apache/johnzon/core/JsonDoubleImpl.java    |  137 +++
 .../johnzon/core/JsonGeneratorFactoryImpl.java     |   79 ++
 .../org/apache/johnzon/core/JsonGeneratorImpl.java |  827 +++++++++++++++
 .../apache/johnzon/core/JsonInMemoryParser.java    |  314 ++++++
 .../org/apache/johnzon/core/JsonLocationImpl.java  |   80 ++
 .../java/org/apache/johnzon/core/JsonLongImpl.java |  121 +++
 .../apache/johnzon/core/JsonMergePatchDiff.java    |   81 ++
 .../apache/johnzon/core/JsonMergePatchImpl.java    |   80 ++
 .../org/apache/johnzon/core/JsonNumberImpl.java    |  132 +++
 .../apache/johnzon/core/JsonObjectBuilderImpl.java |  178 ++++
 .../org/apache/johnzon/core/JsonObjectImpl.java    |  206 ++++
 .../apache/johnzon/core/JsonParserFactoryImpl.java |  137 +++
 .../apache/johnzon/core/JsonPatchBuilderImpl.java  |  181 ++++
 .../org/apache/johnzon/core/JsonPatchDiff.java     |  102 ++
 .../org/apache/johnzon/core/JsonPatchImpl.java     |  213 ++++
 .../org/apache/johnzon/core/JsonPointerImpl.java   |  484 +++++++++
 .../org/apache/johnzon/core/JsonPointerUtil.java   |   49 +
 .../org/apache/johnzon/core/JsonProviderImpl.java  |  384 +++++++
 .../apache/johnzon/core/JsonReaderFactoryImpl.java |   63 ++
 .../org/apache/johnzon/core/JsonReaderImpl.java    |  261 +++++
 .../apache/johnzon/core/JsonStreamParserImpl.java  | 1071 ++++++++++++++++++++
 .../org/apache/johnzon/core/JsonStringImpl.java    |   89 ++
 .../apache/johnzon/core/JsonWriterFactoryImpl.java |   66 ++
 .../org/apache/johnzon/core/JsonWriterImpl.java    |   92 ++
 .../core/RFC4627AwareInputStreamReader.java        |  141 +++
 .../org/apache/johnzon/core/SerializableValue.java |   56 +
 .../java/org/apache/johnzon/core/SimpleStack.java  |   61 ++
 src/main/java/org/apache/johnzon/core/Strings.java |  127 +++
 .../johnzon/core/ThreadLocalBufferCache.java       |   53 +
 70 files changed, 9699 insertions(+), 20 deletions(-)

diff --git a/pom.xml b/pom.xml
index 080b849..2761c6a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -15,7 +15,7 @@
     <parent>
         <groupId>org.apache.sling</groupId>
         <artifactId>sling</artifactId>
-        <version>30</version>
+        <version>32</version>
         <relativePath />
     </parent>
 
@@ -38,17 +38,44 @@
                 <groupId>org.apache.felix</groupId>
                 <artifactId>maven-bundle-plugin</artifactId>
                 <extensions>true</extensions>
+                <executions>
+                    <execution>
+                        <id>bundle-manifest</id>
+                        <phase>process-classes</phase>
+                        <goals>
+                            <goal>manifest</goal>
+                        </goals>
+                    </execution>
+                </executions>
                 <configuration>
+                    <skip>true</skip>
                     <instructions>
                         <Export-Package>
-                            javax.json.*;-split-package:=first
+                            javax.json;-split-package:=first,
+                            javax.json.stream;-split-package:=merge-first,
+                            javax.json.spi;-split-package:=first
                         </Export-Package>
                         <Private-Package>org.apache.johnzon.core</Private-Package>
                         <Import-Package />
+                        <_noee>true</_noee>
                     </instructions>
                 </configuration>
             </plugin>
             <plugin>
+                <artifactId>maven-jar-plugin</artifactId>
+                <configuration>
+                    <forceCreation>true</forceCreation>
+                    <archive>
+                        <manifestFile>
+                            ${project.build.outputDirectory}/META-INF/MANIFEST.MF
+                        </manifestFile>
+                        <manifestEntries>
+                            <Require-Capability>osgi.ee;filter:="(&amp;(osgi.ee=JavaSE)(version=1.7))"</Require-Capability>
+                        </manifestEntries>
+                    </archive>
+                </configuration>
+            </plugin>
+            <plugin>
                 <groupId>org.apache.rat</groupId>
                 <artifactId>apache-rat-plugin</artifactId>
                 <configuration>
@@ -65,11 +92,5 @@
             <artifactId>geronimo-json_1.1_spec</artifactId>
             <version>1.0</version>
         </dependency>
-        <dependency>
-            <groupId>org.apache.johnzon</groupId>
-            <artifactId>johnzon-core</artifactId>
-            <version>1.1.1</version>
-            <scope>provided</scope>
-        </dependency>
     </dependencies>
 </project>
diff --git a/src/main/java/java/util/function/Function.java b/src/main/java/java/util/function/Function.java
new file mode 100644
index 0000000..79877d6
--- /dev/null
+++ b/src/main/java/java/util/function/Function.java
@@ -0,0 +1,23 @@
+/*
+ * 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 java.util.function;
+
+public interface Function<V,R>{
+    R apply(V v);
+}
diff --git a/src/main/java/java/util/stream/Stream.java b/src/main/java/java/util/stream/Stream.java
new file mode 100644
index 0000000..7653f24
--- /dev/null
+++ b/src/main/java/java/util/stream/Stream.java
@@ -0,0 +1,22 @@
+/*
+ * 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 java.util.stream;
+
+public interface Stream<T> {
+}
diff --git a/src/main/java/javax/json/Json.java b/src/main/java/javax/json/Json.java
new file mode 100644
index 0000000..6675879
--- /dev/null
+++ b/src/main/java/javax/json/Json.java
@@ -0,0 +1,359 @@
+/*
+ * 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 javax.json;
+
+import javax.json.spi.JsonProvider;
+import javax.json.stream.JsonGenerator;
+import javax.json.stream.JsonGeneratorFactory;
+import javax.json.stream.JsonParser;
+import javax.json.stream.JsonParserFactory;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Collection;
+import java.util.Map;
+
+public final class Json
+{
+    private Json()
+    {
+        // no-op
+    }
+
+    public static JsonParser createParser(Reader reader)
+    {
+        return JsonProvider.provider().createParser(reader);
+    }
+
+    public static JsonParser createParser(InputStream in)
+    {
+        return JsonProvider.provider().createParser(in);
+    }
+
+    public static JsonGenerator createGenerator(Writer writer)
+    {
+        return JsonProvider.provider().createGenerator(writer);
+    }
+
+    public static JsonGenerator createGenerator(OutputStream out)
+    {
+        return JsonProvider.provider().createGenerator(out);
+    }
+
+    public static JsonParserFactory createParserFactory(Map<String, ?> config)
+    {
+        return JsonProvider.provider().createParserFactory(config);
+    }
+
+    public static JsonGeneratorFactory createGeneratorFactory(Map<String, ?> config)
+    {
+        return JsonProvider.provider().createGeneratorFactory(config);
+    }
+
+    public static JsonWriter createWriter(Writer writer)
+    {
+        return JsonProvider.provider().createWriter(writer);
+    }
+
+    public static JsonWriter createWriter(OutputStream out)
+    {
+        return JsonProvider.provider().createWriter(out);
+    }
+
+    public static JsonReader createReader(Reader reader)
+    {
+        return JsonProvider.provider().createReader(reader);
+    }
+
+    public static JsonReader createReader(InputStream in)
+    {
+        return JsonProvider.provider().createReader(in);
+    }
+
+    public static JsonReaderFactory createReaderFactory(Map<String, ?> config)
+    {
+        return JsonProvider.provider().createReaderFactory(config);
+    }
+
+    public static JsonWriterFactory createWriterFactory(Map<String, ?> config)
+    {
+        return JsonProvider.provider().createWriterFactory(config);
+    }
+
+    public static JsonArrayBuilder createArrayBuilder()
+    {
+        return JsonProvider.provider().createArrayBuilder();
+    }
+
+    /**
+     * Create an empty JsonObjectBuilder
+     *
+     * @since 1.0
+     */
+    public static JsonObjectBuilder createObjectBuilder()
+    {
+        return JsonProvider.provider().createObjectBuilder();
+    }
+
+    /**
+     * Creates a JSON object builder, initialized with the specified JsonObject.
+     *
+     * @since 1.1
+     */
+    public static JsonObjectBuilder createObjectBuilder(JsonObject object)
+    {
+        return JsonProvider.provider().createObjectBuilder(object);
+    }
+
+    /**
+     * Creates a JSON object builder, initialized with the specified Map.
+     *
+     * @since 1.1
+     */
+    public static JsonObjectBuilder createObjectBuilder(Map<String, Object> map)
+    {
+        return JsonProvider.provider().createObjectBuilder(map);
+    }
+
+    public static JsonBuilderFactory createBuilderFactory(Map<String, ?> config)
+    {
+        return JsonProvider.provider().createBuilderFactory(config);
+    }
+
+    /**
+     * Creates a JSON array builder, initialized with an initial content
+     * taken from a JsonArray
+     *
+     * @param initialData the initial array in the builder
+     * @return a JSON array builder
+     * @since 1.1
+     */
+    public static JsonArrayBuilder createArrayBuilder(JsonArray initialData)
+    {
+        return JsonProvider.provider().createArrayBuilder(initialData);
+    }
+
+    /**
+     * Creates a JSON array builder, initialized with an initial content
+     *
+     * @param initialData the initial array in the builder
+     * @return a JSON array builder
+     * @since 1.1
+     */
+    public static JsonArrayBuilder createArrayBuilder(Collection<?> initialData)
+    {
+        return JsonProvider.provider().createArrayBuilder(initialData);
+    }
+
+    public static JsonString createValue(String value)
+    {
+        return JsonProvider.provider().createValue(value);
+    }
+
+    public static JsonNumber createValue(int value)
+    {
+        return JsonProvider.provider().createValue(value);
+    }
+
+    public static JsonNumber createValue(long value)
+    {
+        return JsonProvider.provider().createValue(value);
+    }
+
+    public static JsonNumber createValue(double value)
+    {
+        return JsonProvider.provider().createValue(value);
+    }
+
+    public static JsonNumber createValue(BigDecimal value)
+    {
+        return JsonProvider.provider().createValue(value);
+    }
+
+    public static JsonNumber createValue(BigInteger value)
+    {
+        return JsonProvider.provider().createValue(value);
+    }
+
+    /**
+     * Create a {@link JsonPatch} as defined in
+     * <a href="https://tools.ietf.org/html/rfc6902">RFC-6902</a>.
+     *
+     * @param array with the patch operations
+     * @return the JsonPatch based on the given operations
+     * @since 1.1
+     */
+    public static JsonPatch createPatch(JsonArray array)
+    {
+        return JsonProvider.provider().createPatch(array);
+    }
+
+    /**
+     * Create a {@link JsonPatch} by comparing the source to the target as defined in
+     * <a href="https://tools.ietf.org/html/rfc6902">RFC-6902</a>.
+     * <p>
+     * Applying this {@link JsonPatch} to the source you will give you the target.
+     *
+     * @see #createPatch(JsonArray)
+     * @since 1.1
+     */
+    public static JsonPatch createDiff(JsonStructure source, JsonStructure target)
+    {
+        return JsonProvider.provider().createDiff(source, target);
+    }
+
+    /**
+     * Create a new JsonPatchBuilder
+     *
+     * @since 1.1
+     */
+    public static JsonPatchBuilder createPatchBuilder()
+    {
+        return JsonProvider.provider().createPatchBuilder();
+    }
+
+    /**
+     * Create a new JsonPatchBuilder
+     *
+     * @param initialData the initial patch operations
+     * @since 1.1
+     */
+    public static JsonPatchBuilder createPatchBuilder(JsonArray initialData)
+    {
+        return JsonProvider.provider().createPatchBuilder(initialData);
+    }
+
+    /**
+     * Creates JSON Merge Patch (<a href="http://tools.ietf.org/html/rfc7396">RFC 7396</a>)
+     * from a specified {@link JsonValue}.
+     * Create a merged patch by comparing the source to the target.
+     * <p>
+     * Applying this JsonPatch to the source will give you the target.
+     * <p>
+     * If you have a JSON like
+     * <pre>
+     * {
+     *   "a": "b",
+     *   "c": {
+     *     "d": "e",
+     *     "f": "g"
+     *   }
+     * }
+     * </pre>
+     * <p>
+     * Then you can change the value of "a" and removing "f" by sending:
+     * <pre>
+     * {
+     *   "a":"z",
+     *   "c": {
+     *     "f": null
+     *   }
+     * }
+     * </pre>
+     *
+     * @param patch the patch
+     * @return a JSON Merge Patch
+     * @since 1.1
+     */
+    public static JsonMergePatch createMergePatch(JsonValue patch)
+    {
+        return JsonProvider.provider().createMergePatch(patch);
+    }
+
+    /**
+     * Create a JSON Merge Patch (<a href="http://tools.ietf.org/html/rfc7396">RFC 7396</a>)
+     * from the source and target {@link JsonValue JsonValues}.
+     * Create a merged patch by comparing the source to the target.
+     * <p>
+     * Applying this JsonPatch to the source will give you the target.
+     * <p>
+     * If you have a JSON like
+     * <pre>
+     * {
+     *   "a": "b",
+     *   "c": {
+     *     "d": "e",
+     *     "f": "g"
+     *   }
+     * }
+     * </pre>
+     * <p>
+     * Then you can change the value of "a" and removing "f" by sending:
+     * <pre>
+     * {
+     *   "a":"z",
+     *   "c": {
+     *     "f": null
+     *   }
+     * }
+     * </pre>
+     *
+     * @param source the source
+     * @param target the target
+     * @return a JSON Merge Patch
+     * @since 1.1
+     */
+    public static JsonMergePatch createMergeDiff(JsonValue source, JsonValue target)
+    {
+        return JsonProvider.provider().createMergeDiff(source, target);
+    }
+
+    /**
+     * Create a {@link JsonPointer} for the given path
+     *
+     * @since 1.1
+     */
+    public static JsonPointer createPointer(String path)
+    {
+        return JsonProvider.provider().createPointer(path);
+    }
+
+    /**
+     * @param pointer to encode
+     * @return the properly encoded JsonPointer string
+     * @since 1.1
+     */
+    public static String encodePointer(String pointer)
+    {
+        if (pointer == null || pointer.length() == 0)
+        {
+            return pointer;
+        }
+
+        return pointer.replace("~", "~0").replace("/", "~1");
+    }
+
+    /**
+     * @param escapedPointer
+     * @return the de-escaped JsonPointer
+     *
+     * @since 1.1
+     */
+    public static String decodePointer(String escapedPointer)
+    {
+        if (escapedPointer == null || escapedPointer.length() == 0)
+        {
+            return escapedPointer;
+        }
+
+        return escapedPointer.replace("~1", "/").replace("~0", "~");
+    }
+}
diff --git a/src/main/java/javax/json/JsonArray.java b/src/main/java/javax/json/JsonArray.java
new file mode 100644
index 0000000..4955214
--- /dev/null
+++ b/src/main/java/javax/json/JsonArray.java
@@ -0,0 +1,153 @@
+/*
+ * 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 javax.json;
+
+import java.util.List;
+import java.util.function.Function;
+
+/**
+ * A JsonArray e.g.
+ * <pre>
+ * [1,5,8]
+ * </pre>
+ * or
+ * <pre>
+ *  [
+ *   {"name":"karl", "age": 38},
+ *   {"name":"sue", "age": 42},
+ *  ]
+ * </pre>
+ *
+ *
+ */
+public interface JsonArray extends JsonStructure, List<JsonValue> {
+
+    /**
+     * @return the JsonObject at the given position
+     * @throws IndexOutOfBoundsException if the index is out of range
+     * @throws ClassCastException if the value at the specified position is not
+     *                            assignable to the JsonObject
+     */
+    JsonObject getJsonObject(int index);
+
+    /**
+     * @return the JsonArray at the given position
+     * @throws IndexOutOfBoundsException if the index is out of range
+     * @throws ClassCastException if the value at the specified position is not
+     *                            assignable to the JsonArray
+     */
+    JsonArray getJsonArray(int index);
+
+    /**
+     * @return the JsonNumber at the given position
+     * @throws IndexOutOfBoundsException if the index is out of range
+     * @throws ClassCastException if the value at the specified position is not
+     *                            assignable to the JsonNumber
+     */
+    JsonNumber getJsonNumber(int index);
+
+    /**
+     * @return the JsonString at the given position
+     * @throws IndexOutOfBoundsException if the index is out of range
+     * @throws ClassCastException if the value at the specified position is not
+     *                            assignable to the JsonString
+     */
+    JsonString getJsonString(int index);
+
+    /**
+     * @return the respective JsonValue at the given position
+     * @throws IndexOutOfBoundsException if the index is out of range
+     * @throws ClassCastException if the value at the specified position is not
+     *                            assignable to the given slazz
+     */
+    <T extends JsonValue> List<T> getValuesAs(Class<T> clazz);
+
+    /**
+     * Returns a list for the array. The value and the type of the elements
+     * in the list is specified by the {@code func} argument.
+     * <p>This method can be used to obtain a list of the unwrapped types, such as
+     * <pre>{@code
+     *     List<String> strings = ary1.getValuesAs(JsonString::getString);
+     *     List<Integer> ints = ary2.getValuesAs(JsonNumber::intValue);
+     * } </pre>
+     * It can also be used to obtain a list of simple projections, such as
+     * <pre> {@code
+     *     Lsit<Integer> stringsizes = arr.getValueAs((JsonString v) -> v.getString().length();
+     * } </pre>
+     * @param <K> The element type (must be a subtype of JsonValue) of this JsonArray.
+     * @param <T> The element type of the returned List
+     * @param func The function that maps the elements of this JsonArray to the target elements.
+     * @return A List of the specified values and type.
+     * @throws ClassCastException if the {@code JsonArray} contains a value of wrong type
+     */
+    <T, K extends JsonValue> List<T> getValuesAs(Function<K, T> func);
+
+    /**
+     * @return the native String at the given position
+     * @throws IndexOutOfBoundsException if the index is out of range
+     * @throws ClassCastException if the value at the specified position is not
+     *                            assignable to a String
+     */
+    String getString(int index);
+
+    /**
+     * @return the native String at the given position or the defaultValue if null
+     * @throws IndexOutOfBoundsException if the index is out of range
+     * @throws ClassCastException if the value at the specified position is not
+     *                            assignable to a String
+     */
+    String getString(int index, String defaultValue);
+
+    /**
+     * @return the native int value at the given position
+     * @throws IndexOutOfBoundsException if the index is out of range
+     * @throws ClassCastException if the value at the specified position is not
+     *                            assignable to an int
+     */
+    int getInt(int index);
+
+    /**
+     * @return the native int value at the given position or the defaultValue if null
+     * @throws IndexOutOfBoundsException if the index is out of range
+     * @throws ClassCastException if the value at the specified position is not
+     *                            assignable to an int
+     */
+    int getInt(int index, int defaultValue);
+
+    /**
+     * @return the native boolean value at the given position
+     * @throws IndexOutOfBoundsException if the index is out of range
+     * @throws ClassCastException if the value at the specified position is not
+     *                            assignable to a boolean
+     */
+    boolean getBoolean(int index);
+
+    /**
+     * @return the native boolean value at the given position or the defaultValue if null
+     * @throws IndexOutOfBoundsException if the index is out of range
+     * @throws ClassCastException if the value at the specified position is not
+     *                            assignable to a boolean
+     */
+    boolean getBoolean(int index, boolean defaultValue);
+
+    /**
+     * @return whether the value at the given position is {@link JsonValue#NULL}.
+     * @throws IndexOutOfBoundsException if the index is out of range
+     */
+    boolean isNull(int index);
+}
+
diff --git a/src/main/java/javax/json/JsonArrayBuilder.java b/src/main/java/javax/json/JsonArrayBuilder.java
new file mode 100644
index 0000000..3de276f
--- /dev/null
+++ b/src/main/java/javax/json/JsonArrayBuilder.java
@@ -0,0 +1,94 @@
+/*
+ * 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 javax.json;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+public interface JsonArrayBuilder {
+    JsonArrayBuilder add(JsonValue value);
+
+    JsonArrayBuilder add(String value);
+
+    JsonArrayBuilder add(BigDecimal value);
+
+    JsonArrayBuilder add(BigInteger value);
+
+    JsonArrayBuilder add(int value);
+
+    JsonArrayBuilder add(long value);
+
+    JsonArrayBuilder add(double value);
+
+    JsonArrayBuilder add(boolean value);
+
+    JsonArrayBuilder addNull();
+
+    JsonArrayBuilder add(JsonObjectBuilder builder);
+
+    JsonArrayBuilder add(JsonArrayBuilder builder);
+
+    JsonArray build();
+
+    JsonArrayBuilder addAll(JsonArrayBuilder builder);
+
+    JsonArrayBuilder add(int index, JsonValue value);
+
+    JsonArrayBuilder add(int index, String value);
+
+    JsonArrayBuilder add(int index, BigDecimal value);
+
+    JsonArrayBuilder add(int index, BigInteger value);
+
+    JsonArrayBuilder add(int index, int value);
+
+    JsonArrayBuilder add(int index, long value);
+
+    JsonArrayBuilder add(int index, double value);
+
+    JsonArrayBuilder add(int index, boolean value);
+
+    JsonArrayBuilder addNull(int index);
+
+    JsonArrayBuilder add(int index, JsonObjectBuilder builder);
+
+    JsonArrayBuilder add(int index, JsonArrayBuilder builder);
+
+    JsonArrayBuilder set(int index, JsonValue value);
+
+    JsonArrayBuilder set(int index, String value);
+
+    JsonArrayBuilder set(int index, BigDecimal value);
+
+    JsonArrayBuilder set(int index, BigInteger value);
+
+    JsonArrayBuilder set(int index, int value);
+
+    JsonArrayBuilder set(int index, long value);
+
+    JsonArrayBuilder set(int index, double value);
+
+    JsonArrayBuilder set(int index, boolean value);
+
+    JsonArrayBuilder setNull(int index);
+
+    JsonArrayBuilder set(int index, JsonObjectBuilder builder);
+
+    JsonArrayBuilder set(int index, JsonArrayBuilder builder);
+
+    JsonArrayBuilder remove(int index);
+}
diff --git a/src/main/java/javax/json/JsonBuilderFactory.java b/src/main/java/javax/json/JsonBuilderFactory.java
new file mode 100644
index 0000000..0958d26
--- /dev/null
+++ b/src/main/java/javax/json/JsonBuilderFactory.java
@@ -0,0 +1,81 @@
+/*
+ * 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 javax.json;
+
+import java.util.Map;
+
+public interface JsonBuilderFactory {
+    /**
+     * @return a new empty JsonObjectBuilder
+     */
+    JsonObjectBuilder createObjectBuilder();
+
+    /**
+     * @return a new empty JsonArrayBuilder
+     */
+    JsonArrayBuilder createArrayBuilder();
+
+    /**
+     * @return the config which got used when creating this builder factory.
+     */
+    Map<String, ?> getConfigInUse();
+
+    /**
+     * Create a JsonObjectBuilder filled with the given initial data.
+     *
+     * @throws NullPointerException if initialData is {@code null}
+     *
+     * @return a new pre initialised JsonObjectBuilder
+     *
+     * @since 1.1
+     */
+    JsonObjectBuilder createObjectBuilder(JsonObject initialData);
+
+    /**
+     * Create a JsonObjectBuilder filled with the given initial data.
+     *
+     * @throws NullPointerException if initialData is {@code null}
+     *
+     * @return a new pre initialised JsonObjectBuilder
+     *
+     * @since 1.1
+     */
+    JsonObjectBuilder createObjectBuilder(Map<String, Object> initialData);
+
+    /**
+     * Create a JsonArrayBuilder filled with the given initial data.
+     *
+     * @throws NullPointerException if initialData is {@code null}
+     *
+     * @return a new pre initialised JsonArrayBuilder
+     *
+     * @since 1.1
+     */
+    JsonArrayBuilder createArrayBuilder(JsonArray initialData);
+
+    /**
+     * Create a {@link JsonArrayBuilder} which is filled with the given initial content.
+     * @param initialData the content to immediately add to the JsonArrayBuilder
+     *
+     * @throws NullPointerException if initialData is {@code null}
+     *
+     * @return a new pre initialised JsonArrayBuilder
+     * @since 1.1
+     */
+    JsonArrayBuilder createArrayBuilder(java.util.Collection<?> initialData);
+}
+
diff --git a/src/main/java/javax/json/JsonException.java b/src/main/java/javax/json/JsonException.java
new file mode 100644
index 0000000..47c4d61
--- /dev/null
+++ b/src/main/java/javax/json/JsonException.java
@@ -0,0 +1,27 @@
+/*
+ * 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 javax.json;
+
+public class JsonException extends RuntimeException {
+    public JsonException(final String message) {
+        super(message);
+    }
+
+    public JsonException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/src/main/java/javax/json/JsonMergePatch.java b/src/main/java/javax/json/JsonMergePatch.java
new file mode 100644
index 0000000..0ff3b7d
--- /dev/null
+++ b/src/main/java/javax/json/JsonMergePatch.java
@@ -0,0 +1,40 @@
+/*
+ * 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 javax.json;
+
+/**
+ * A JSON MergePatch as defined in http://tools.ietf.org/html/rfc7396
+ *
+ * @see javax.json.spi.JsonProvider#createMergePatch(JsonValue)
+ * @see javax.json.spi.JsonProvider#createMergeDiff(JsonValue, JsonValue)
+ */
+public interface JsonMergePatch  {
+
+    /**
+     * Applies the current JsonMergePatch to the given value
+     * @param valueToApplyPatchOn the base value. Will not get changed itself.
+     * @return the patched new JsonValue
+     */
+    JsonValue apply(JsonValue valueToApplyPatchOn);
+
+    /**
+     * @return the Patch as JsonValue
+     */
+    JsonValue toJsonValue();
+}
+
diff --git a/src/main/java/javax/json/JsonNumber.java b/src/main/java/javax/json/JsonNumber.java
new file mode 100644
index 0000000..ab0ace2
--- /dev/null
+++ b/src/main/java/javax/json/JsonNumber.java
@@ -0,0 +1,61 @@
+/*
+ * 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 javax.json;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * JsonValue which represents a number.
+ *
+ * The decimal point is defined as dot '.'.
+ *
+ * @see <a href="https://tools.ietf.org/html/rfc4627">RFC-4627 JSON Specification</a>
+ */
+public interface JsonNumber extends JsonValue {
+    boolean isIntegral();
+
+    int intValue();
+
+    int intValueExact();
+
+    long longValue();
+
+    long longValueExact();
+
+    BigInteger bigIntegerValue();
+
+    BigInteger bigIntegerValueExact();
+
+    double doubleValue();
+
+    BigDecimal bigDecimalValue();
+
+    /**
+     * @since 1.1
+     */
+    Number numberValue();
+
+    @Override
+    String toString();
+
+    @Override
+    boolean equals(Object obj);
+
+    @Override
+    int hashCode();
+}
diff --git a/src/main/java/javax/json/JsonObject.java b/src/main/java/javax/json/JsonObject.java
new file mode 100644
index 0000000..c1e6f82
--- /dev/null
+++ b/src/main/java/javax/json/JsonObject.java
@@ -0,0 +1,103 @@
+/*
+ * 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 javax.json;
+
+import java.util.Map;
+
+/**
+ * A JsonObject, e.g.
+ * <pre>
+ * {
+ *     "name":"karl",
+ *     "age":38,
+ *     "address": {
+ *         "street":"dummystreet"
+ *         "housenumber":12
+ *     }
+ * }
+ * </pre>
+ *
+ * A JsonObject is always also a Map which uses the attribute names as key mapping
+ * to their JsonValues.
+ */
+public interface JsonObject extends JsonStructure, Map<String, JsonValue> {
+
+    /**
+     * @return the JsonArray with the given name or {@code null} if there is no attribute with that name
+     * @throws ClassCastException if the JsonValue cannot be correctly cast
+     */
+    JsonArray getJsonArray(String name);
+
+    /**
+     * @return the JsonObject with the given name or {@code null} if there is no attribute with that name
+     * @throws ClassCastException if the JsonValue cannot be correctly cast
+     */
+    JsonObject getJsonObject(String name);
+
+    /**
+     * @return the JsonNumber with the given name or {@code null} if there is no attribute with that name
+     * @throws ClassCastException if the JsonValue cannot be correctly cast
+     */
+    JsonNumber getJsonNumber(String name);
+
+    /**
+     * @return the JsonString with the given name or {@code null} if there is no attribute with that name
+     * @throws ClassCastException if the JsonValue cannot be correctly cast
+     */
+    JsonString getJsonString(String name);
+
+    /**
+     * @return the native string with the given name or {@code null} if there is no attribute with that name
+     * @throws ClassCastException if the JsonValue cannot be correctly cast
+     */
+    String getString(String name);
+
+    /**
+     * @return the native string with the given name or the default value if there is no attribute with that name
+     * @throws ClassCastException if the JsonValue cannot be correctly cast
+     */
+    String getString(String name, String defaultValue);
+
+    /**
+     * @return the int with the given name or {@code null} if there is no attribute with that name
+     * @throws ClassCastException if the JsonValue cannot be correctly cast
+     */
+    int getInt(String name);
+
+    /**
+     * @return the int with the given name or the default value if there is no attribute with that name
+     * @throws ClassCastException if the JsonValue cannot be correctly cast
+     */
+    int getInt(String name, int defaultValue);
+
+    /**
+     * @return the boolean with the given name or {@code null} if there is no attribute with that name
+     * @throws ClassCastException if the JsonValue cannot be correctly cast
+     */
+    boolean getBoolean(String name);
+
+    /**
+     * @return the boolean with the given name or the default value if there is no attribute with that name
+     * @throws ClassCastException if the JsonValue cannot be correctly cast
+     */
+    boolean getBoolean(String name, boolean defaultValue);
+
+    /**
+     * @return whether the attribute with the given name is {@link JsonValue#NULL}
+     */
+    boolean isNull(String name);
+}
diff --git a/src/main/java/javax/json/JsonObjectBuilder.java b/src/main/java/javax/json/JsonObjectBuilder.java
new file mode 100644
index 0000000..e472a64
--- /dev/null
+++ b/src/main/java/javax/json/JsonObjectBuilder.java
@@ -0,0 +1,148 @@
+/*
+ * 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 javax.json;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * A JsonObjectBuilder can be used to build {@link JsonObject JsonObjects}.
+ * Instances are not thread safe.
+ *
+ * Calling any of those methods with either the {@code name} or {@code value} param as {@code null}
+ * will result in a {@code NullPointerException}
+ */
+public interface JsonObjectBuilder {
+    /**
+     * Add the given JsonValue value to the JsonObject to be created.
+     * If a value with that name already exists it will be replaced by the new value.
+     * @param name the JSON attribute name
+     * @param value the JsonValue to add
+     * @return the current JsonObjectBuilder
+     */
+    JsonObjectBuilder add(String name, JsonValue value);
+
+    /**
+     * Add the given String value to the JsonObject to be created.
+     * If a value with that name already exists it will be replaced by the new value.
+     * @param name the JSON attribute name
+     * @param value the String value to add
+     * @return the current JsonObjectBuilder
+     */
+    JsonObjectBuilder add(String name, String value);
+
+    /**
+     * Add the given BigInteger value to the JsonObject to be created.
+     * If a value with that name already exists it will be replaced by the new value.
+     * @param name the JSON attribute name
+     * @param value the BigInteger value to add
+     * @return the current JsonObjectBuilder
+     */
+    JsonObjectBuilder add(String name, BigInteger value);
+
+    /**
+     * Add the given BigDecimal value to the JsonObject to be created.
+     * If a value with that name already exists it will be replaced by the new value.
+     * @param name the JSON attribute name
+     * @param value the BigDecimal value to add
+     * @return the current JsonObjectBuilder
+     */
+    JsonObjectBuilder add(String name, BigDecimal value);
+
+    /**
+     * Add the given int value to the JsonObject to be created.
+     * If a value with that name already exists it will be replaced by the new value.
+     * @param name the JSON attribute name
+     * @param value to add
+     * @return the current JsonObjectBuilder
+     */
+    JsonObjectBuilder add(String name, int value);
+
+    /**
+     * Add the given long value to the JsonObject to be created.
+     * If a value with that name already exists it will be replaced by the new value.
+     * @param name the JSON attribute name
+     * @param value to add
+     * @return the current JsonObjectBuilder
+     */
+    JsonObjectBuilder add(String name, long value);
+
+    /**
+     * Add the given double value to the JsonObject to be created.
+     * If a value with that name already exists it will be replaced by the new value.
+     * @param name the JSON attribute name
+     * @param value to add
+     * @return the current JsonObjectBuilder
+     */
+    JsonObjectBuilder add(String name, double value);
+
+    /**
+     * Add the given boolean value to the JsonObject to be created.
+     * If a value with that name already exists it will be replaced by the new value.
+     * @param name the JSON attribute name
+     * @param value to add
+     * @return the current JsonObjectBuilder
+     */
+    JsonObjectBuilder add(String name, boolean value);
+
+    /**
+     * Add a {@link JsonValue#NULL} value to the JsonObject to be created.
+     * If a value with that name already exists it will be replaced by the null value.
+     * @param name the JSON attribute name
+     * @return the current JsonObjectBuilder
+     */
+    JsonObjectBuilder addNull(String name);
+
+    /**
+     * Use the given {@link JsonObjectBuilder} to create a {@link JsonObject} which will be
+     * added to the JsonObject to be created by this builder.
+     * If a value with that name already exists it will be replaced by the new value.
+     * @param name the JSON attribute name
+     * @param builder for creating the JsonObject to add
+     * @return the current JsonObjectBuilder
+     */
+    JsonObjectBuilder add(String name, JsonObjectBuilder builder);
+
+    /**
+     * Use the given {@link JsonArrayBuilder} to create a {@link JsonArray} which will be
+     * added to the JsonObject to be created by this builder.
+     * If a value with that name already exists it will be replaced by the new value.
+     * @param name the JSON attribute name
+     * @param builder for creating the JsonArray to add
+     * @return the current JsonObjectBuilder
+     */
+    JsonObjectBuilder add(String name, JsonArrayBuilder builder);
+
+    /**
+     * @return a {@link JsonObject} based on the added values.
+     */
+    JsonObject build();
+
+    /**
+     * Add all of the attributes of the given {@link JsonObjectBuilder} to the current one
+     *
+     * @since 1.1
+     */
+    JsonObjectBuilder addAll(JsonObjectBuilder builder);
+
+    /**
+     * Remove the attribute with the given name from the builder.
+     *
+     * @since 1.1
+     */
+    JsonObjectBuilder remove(String name);
+}
diff --git a/src/main/java/javax/json/JsonPatch.java b/src/main/java/javax/json/JsonPatch.java
new file mode 100644
index 0000000..7a36fcf
--- /dev/null
+++ b/src/main/java/javax/json/JsonPatch.java
@@ -0,0 +1,208 @@
+/*
+ * 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 javax.json;
+
+/**
+ * <p>
+ * JsonPatch is a format for expressing a sequence of operations to apply on
+ * a target JSON document.
+ * </p>
+ * <p>
+ * A JsonPatch is an array of 'operations' in the form e.g.
+ * <p>
+ * <pre>
+ * [
+ *   { "op": "add", "path": "/foo/-", "value": ["abc", "def"] }
+ *   { "path": "/a/b/c", "op": "add", "value": "foo" }
+ * ]
+ * </pre>
+ * <p>
+ * The 'operations' are performed in the order they are in the JsonPatch and applied
+ * to the 'result' JSON document from the previous operation.
+ * <p>
+ *
+ * @see Operation
+ *
+ * @since 1.1
+ */
+public interface JsonPatch {
+
+    /**
+     * <p>
+     * An enumeration of available operations for {@link JsonPatch}.<br>
+     * <p>
+     * NOTICE: the behavoir of some operations depends on which {@link JsonValue} they are performed.
+     * for details please check the documentation of the very operation.
+     *
+     * @see <a href="http://tools.ietf.org/html/rfc6902">RFC-6902</a>
+     *
+     * @since 1.1
+     */
+    enum Operation {
+
+        /**
+         * <p>
+         * required members are:
+         * <ul>
+         * <li>"op": "add"</li>
+         * <li>"path": "path/to/add"</li>
+         * <li>"value": "{@link JsonValue}ToAdd"</li>
+         * </ul>
+         * <p>
+         * if the "path/to" does not exist, this operation will result in an error.<br>
+         * if the "path/to/add" already exists, the value will be <strong>replaced</strong>
+         * <p>
+         * for {@link JsonArray}s the new value will be inserted at the specified index
+         * and the element(s) at/after are shifted to the right. the '-' character is used to append the value
+         * at the and of the {@link JsonArray}.
+         */
+        ADD("add"),
+
+        /**
+         * <p>
+         * required members are:
+         * <ul>
+         * <li>"op": "remove"</li>
+         * <li>"path": "path/to/remove"</li>
+         * </ul>
+         * <p>
+         * if the "path/to/remove" does not exist, the operation will fail.
+         * <p>
+         * for {@link JsonArray}s the values after the removed value are shifted to the left
+         */
+        REMOVE("remove"),
+
+        /**
+         * <p>
+         * required members are:
+         * <ul>
+         * <li>"op": "replace"</li>
+         * <li>"path": "path/to/replace"</li>
+         * <li>"value": "the new {@link JsonValue}"</li>
+         * </ul>
+         * <p>
+         * this operation is identical to {@link #REMOVE} followed by {@link #ADD}
+         */
+        REPLACE("replace"),
+
+        /**
+         * <p>
+         * required members are:
+         * <ul>
+         * <li>"op": "move"</li>
+         * <li>"from": "path/to/move/from"</li>
+         * <li>"path": "path/to/move/to"</li>
+         * </ul>
+         * <p>
+         * the operation will fail it the "path/to/move/from" does not exist
+         * <p>
+         * NOTICE: a location can not be moved into one of it's children. (from /a/b/c to /a/b/c/d)
+         * <p>
+         * this operation is identical to {@link #REMOVE} from "from" and {@link #ADD} to the "path"
+         */
+        MOVE("move"),
+
+        /**
+         * <p>
+         * required members are:
+         * <ul>
+         * <li>"op": "copy"</li>
+         * <li>"from": "path/to/copy/from"</li>
+         * <li>"path": "path/to/add"</li>
+         * </ul>
+         * <p>
+         * the operation will result in an error if the "from" location does not exist
+         * <p>
+         * this operation is identical to {@link #ADD} with the "from" value
+         */
+        COPY("copy"),
+
+        /**
+         * <p>
+         * required members are:
+         * <ul>
+         * <li>"op": "test"</li>
+         * <li>"path": "/path/to/test"</li>
+         * <li>"value": "{@link JsonValue} to test"</li>
+         * </ul>
+         * <p>
+         * this operation fails, if the value is NOT equal with the /path/to/test
+         * <p>
+         * ordering of the elements in a {@link JsonObject} is NOT significant however
+         * the position of an element in a {@link JsonArray} is significant for equality.
+         */
+        TEST("test");
+
+
+        private final String operationName;
+
+
+        Operation(String operationName) {
+            this.operationName = operationName;
+        }
+
+
+        /**
+         * @return the JSON operation name
+         */
+        public String operationName() {
+            return operationName;
+        }
+
+        /**
+         * Returns the {@link Operation} for the given {@code operationName}. If no {@link Operation}
+         * is present, a {@link JsonException} will be thrown.
+         *
+         * @param operationName {@code operationName} to convert to the enum constant.
+         *
+         * @return the {@link Operation Operation-constant} for given {@code operationName}
+         *
+         * @throws JsonException if no {@link Operation} has been found for the given {@code operationName}
+         */
+        public static Operation fromOperationName(String operationName) {
+
+            for (Operation op : values()) {
+                if (op.operationName().equalsIgnoreCase(operationName)) {
+                    return op;
+                }
+            }
+
+            throw new JsonException("unknown value for the operationName of the JSON patch operation: " + operationName);
+        }
+    }
+
+
+    /**
+     * Applies the {@link JsonPatch} to the given {@code target}. If the
+     * given {@code target} is {@code null} a {@link NullPointerException}
+     * will be thrown.
+     *
+     * @param target - the target to apply the {@link JsonPatch}
+     *
+     * @return 'patched' {@link JsonStructure}
+     *
+     * @throws NullPointerException if {@code target} is {@code null}
+     */
+    <T extends JsonStructure> T apply(T target);
+
+    /**
+     * @return the JsonPatch as {@link JsonArray}
+     */
+    JsonArray toJsonArray();
+}
+
diff --git a/src/main/java/javax/json/JsonPatchBuilder.java b/src/main/java/javax/json/JsonPatchBuilder.java
new file mode 100644
index 0000000..775e048
--- /dev/null
+++ b/src/main/java/javax/json/JsonPatchBuilder.java
@@ -0,0 +1,193 @@
+/*
+ * 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 javax.json;
+
+/**
+ * TODO this is a final class in the spec, but that makes no sense.
+ * This class can be used to easily create {@link JsonPatch}es.
+ * To get an instance use {@link Json#createPatchBuilder()} or {@link Json#createPatchBuilder(JsonArray)}
+ * The order of the operations corresponds to the order they are builded.
+ * <p>
+ * NOTICE: A JsonPatchBuilder contains state and therefore is NOT threadsafe and should not be used concurrently.
+ * <p>
+ * The following {@link JsonPatch}
+ * <pre>
+ *     "[
+ *         {
+ *           "op": "add",
+ *           "path": "/add/object",
+ *           "value": {
+ *             "foo": "bar"
+ *           }
+ *         },
+ *         {
+ *           "op": "remove",
+ *           "path": "/remove/it"
+ *         },
+ *         {
+ *           "op": "move",
+ *           "path": "move/to",
+ *           "from": "move/from"
+ *         }
+ *     ]"
+ * </pre>
+ * can be build with the JsonPatchBuilder
+ * <pre>
+ *
+ *     JsonPatch patch = Json.createJsonPatchBuilder()
+ *                           .add("/add/object", Json.createObjectBuilder()
+ *                                                   .add("foo", "bar")
+ *                                                   .build())
+ *                           .remove("/remove/it")
+ *                           .move("/move/to", "move/from")
+ *                           .build();
+ *
+ * </pre>
+ * <p>
+ * An instance of a JsonPatchBuilder can be reused for another {@link JsonPatch} after
+ * the {@link #build()}-Method was called.
+ */
+public interface JsonPatchBuilder {
+
+    /**
+     * Adds an 'add'-operation to the {@link JsonPatch}
+     *
+     * @param path as {@link JsonPointer} where the value should be added
+     * @param value the value to add
+     *
+     * @return the builder instance for chained method calls
+     *
+     * @throws NullPointerException if the given {@code path} is {@code null}
+     */
+    JsonPatchBuilder add(String path, JsonValue value);
+
+    /**
+     * @see #add(String, JsonValue)
+     */
+    JsonPatchBuilder add(String path, String value);
+
+    /**
+     * @see #add(String, JsonValue)
+     */
+    JsonPatchBuilder add(String path, int value);
+
+    /**
+     * @see #add(String, JsonValue)
+     */
+    JsonPatchBuilder add(String path, boolean value);
+
+
+    /**
+     * Adds a 'remove'-operation to the {@link JsonPatch}
+     *
+     * @param path as {@link JsonPointer} of the value which should get removed
+     *
+     * @return the builder instance for chained method calls
+     *
+     * @throws NullPointerException if the given {@code path} is {@code null}
+     */
+    JsonPatchBuilder remove(String path);
+
+
+    /**
+     * Adds a 'replace'-operation to the {@link JsonPatch}
+     *
+     * @param path as {@link JsonPointer} to the value which should get replaced
+     * @param value the new value
+     *
+     * @return the builder instance for chained method calls
+     *
+     * @throws NullPointerException if the given {@code path} is {@code null}
+     */
+    JsonPatchBuilder replace(String path, JsonValue value);
+
+    /**
+     * @see #replace(String, JsonValue)
+     */
+    JsonPatchBuilder replace(String path, String value);
+
+    /**
+     * @see #replace(String, JsonValue)
+     */
+    JsonPatchBuilder replace(String path, int value);
+
+    /**
+     * @see #replace(String, JsonValue)
+     */
+    JsonPatchBuilder replace(String path, boolean value);
+
+
+    /**
+     * Adds a 'move'-operation to the {@link JsonPatch}
+     *
+     * @param path where the value should get inserted as {@link JsonPointer}
+     * @param from where the value should be taken from as {@link JsonPointer}
+     *
+     * @return the builder instance for chained method calls
+     *
+     * @throws NullPointerException if the given {@code path} is {@code null}
+     *                              if the given {@code from} is {@code null}
+     */
+    JsonPatchBuilder move(String path, String from);
+
+
+    /**
+     * Adds a 'copy'-operation to the {@link JsonPatch}
+     *
+     * @param path where the copied value should get inserted as {@link JsonPointer}
+     * @param from value to copy as {@link JsonPointer}
+     *
+     * @return the builder instance for chained method calls
+     *
+     * @throws NullPointerException if the given {@code path} is {@code null}
+     *                              if the given {@code from} is {@code null}
+     */
+    JsonPatchBuilder copy(String path, String from);
+
+
+    /**
+     * Adds a 'test'-operation to the {@link JsonPointer}
+     *
+     * @param path as {@link JsonPointer} to the value to test
+     * @param value value to test
+
+     * @return the builder instance for chained method calls
+
+     * @throws NullPointerException if the given {@code path} is {@code null}
+     */
+    JsonPatchBuilder test(String path, JsonValue value);
+
+    /**
+     * @see #test(String, JsonValue)
+     */
+    JsonPatchBuilder test(String path, String value);
+
+    /**
+     * @see #test(String, JsonValue)
+     */
+    JsonPatchBuilder test(String path, int value);
+
+    /**
+     * @see #test(String, JsonValue)
+     */
+    JsonPatchBuilder test(String path, boolean value);
+
+
+    JsonPatch build();
+}
+
diff --git a/src/main/java/javax/json/JsonPointer.java b/src/main/java/javax/json/JsonPointer.java
new file mode 100644
index 0000000..a4018d2
--- /dev/null
+++ b/src/main/java/javax/json/JsonPointer.java
@@ -0,0 +1,94 @@
+/*
+ * 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 javax.json;
+
+
+/**
+ * <p>This class is an immutable representation of a JSON Pointer as specified in
+ * <a href="http://tools.ietf.org/html/rfc6901">RFC 6901</a>.
+ *
+ *
+ * <p>JSON Pointer is a string syntax for identifying a specific value
+ * within a JavaScript Object Notation (JSON) document [RFC4627].
+ * JSON Pointer is intended to be easily expressed in JSON string values
+ * as well as Uniform Resource Identifier (URI) [RFC3986] fragment identifiers.
+ *
+ * <p> The method {@link #getValue getValue()} returns the referenced value.
+ * The methods {@link #add add()}, {@link #replace replace()},
+ * and {@link #remove remove()} executes the operations specified in
+ * <a href="http://tools.ietf.org/html/rfc6902">RFC 6902</a>.
+ *
+ * @since 1.1
+ */
+public interface JsonPointer {
+
+    /**
+     * Get the JsonValue at the position referenced by this JsonPointer.
+     * @param target the JSON to apply this JsonPointer on
+     * @return the
+     * @throws JsonException if no value exists at the referenced location
+     * @throws NullPointerException if the target is {@code null}
+     */
+    JsonValue getValue(JsonStructure target);
+
+
+    /**
+     * Add or replace the value at the position referenced by this JsonPointer with
+     * the new value
+     * @param target structure in which the newValue should be added or replaced
+     * @param newValue the new value to set
+     * @param <T>
+     * @return the new structure after modifying the original JsonStrucure
+     * @throws JsonException if no value exists at the referenced location
+     * @throws NullPointerException if the target is {@code null}
+     */
+    <T extends JsonStructure> T add(T target, JsonValue newValue);
+
+
+    /**
+     * Remove the value from the referenced position inside the target JSON.
+     * @param target to remove the value from
+     * @param <T> the type of the passed JsonStructure
+     * @return a new JsonStructure with the value removed from the target
+     * @throws JsonException if no value exists at the referenced location
+     * @throws NullPointerException if the target is {@code null}
+     */
+    <T extends JsonStructure> T remove(T target);
+
+    /**
+     * Replace the value at the position referenced by this JsonPointer with
+     * the newValue.
+     *
+     * @param target structure in which the newValue should be replaced
+     * @param newValue the new value to set
+     * @param <T>
+     * @return the new structure after modifying the original JsonStrucure
+     * @throws JsonException if no value exists at the referenced location
+     * @throws NullPointerException if the target is {@code null}
+     */
+    <T extends JsonStructure> T  replace(T target, JsonValue newValue);
+
+    /**
+     * Verify if the target JSON structure contains a value at the referenced location.
+     * @param target to check
+     * @return {@code true} if there is a value at the referenced location, {@code false} otherwise
+     * @throws NullPointerException if the target is {@code null}
+     */
+    boolean containsValue(JsonStructure target);
+
+}
diff --git a/src/main/java/javax/json/JsonReader.java b/src/main/java/javax/json/JsonReader.java
new file mode 100644
index 0000000..4284410
--- /dev/null
+++ b/src/main/java/javax/json/JsonReader.java
@@ -0,0 +1,32 @@
+/*
+ * 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 javax.json;
+
+import java.io.Closeable;
+
+public interface JsonReader extends Closeable {
+    JsonStructure read();
+
+    JsonObject readObject();
+
+    JsonArray readArray();
+
+    @Override
+    void close();
+    
+    JsonValue readValue();
+}
diff --git a/src/main/java/javax/json/JsonReaderFactory.java b/src/main/java/javax/json/JsonReaderFactory.java
new file mode 100644
index 0000000..8212fe1
--- /dev/null
+++ b/src/main/java/javax/json/JsonReaderFactory.java
@@ -0,0 +1,32 @@
+/*
+ * 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 javax.json;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.nio.charset.Charset;
+import java.util.Map;
+
+public interface JsonReaderFactory {
+    JsonReader createReader(Reader reader);
+
+    JsonReader createReader(InputStream in);
+
+    JsonReader createReader(InputStream in, Charset charset);
+
+    Map<String, ?> getConfigInUse();
+}
diff --git a/src/main/java/javax/json/JsonString.java b/src/main/java/javax/json/JsonString.java
new file mode 100644
index 0000000..868cfad
--- /dev/null
+++ b/src/main/java/javax/json/JsonString.java
@@ -0,0 +1,32 @@
+/*
+ * 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 javax.json;
+
+/**
+ * JsonValue which represents a string.
+ */
+public interface JsonString extends JsonValue {
+    String getString();
+
+    CharSequence getChars();
+
+    @Override
+    boolean equals(Object obj);
+
+    @Override
+    int hashCode();
+}
diff --git a/src/main/java/javax/json/JsonStructure.java b/src/main/java/javax/json/JsonStructure.java
new file mode 100644
index 0000000..c4d353c
--- /dev/null
+++ b/src/main/java/javax/json/JsonStructure.java
@@ -0,0 +1,28 @@
+/*
+ * 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 javax.json;
+
+import javax.json.spi.JsonProvider;
+
+/**
+ * A complex type as JsonValue.
+ * This could either be a {@link JsonArray} or a {@link JsonObject}.
+ */
+public interface JsonStructure extends JsonValue {
+
+    JsonValue getValue(String jsonPointer);
+}
\ No newline at end of file
diff --git a/src/main/java/javax/json/JsonValue.java b/src/main/java/javax/json/JsonValue.java
new file mode 100644
index 0000000..8598a1c
--- /dev/null
+++ b/src/main/java/javax/json/JsonValue.java
@@ -0,0 +1,149 @@
+/*
+ * 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 javax.json;
+
+/**
+ * A single value in a JSON expression.
+ */
+public interface JsonValue {
+
+    /**
+     * The empty JSON object.
+     */
+    JsonObject EMPTY_JSON_OBJECT = Json.createObjectBuilder().build();
+
+    /**
+     * The empty JSON array.
+     */
+    JsonArray EMPTY_JSON_ARRAY = Json.createArrayBuilder().build();
+
+
+    /**
+     * A constant JsonValue for null values
+     */
+    JsonValue NULL = new JsonValue() {
+        @Override
+        public ValueType getValueType() {
+            return ValueType.NULL;
+        }
+
+        @Override
+        public boolean equals(final Object obj) {
+            return obj instanceof JsonValue && getValueType().equals(JsonValue.class.cast(obj).getValueType());
+        }
+
+        @Override
+        public int hashCode() {
+            return ValueType.NULL.hashCode();
+        }
+
+        @Override
+        public String toString() {
+            return "null";
+        }
+
+        public JsonObject asJsonObject() {
+            return JsonObject.class.cast(this);
+        }
+
+        public JsonArray asJsonArray() {
+            return JsonArray.class.cast(this);
+        }
+    };
+
+    /**
+     * A constant JsonValue for TRUE
+     */
+    JsonValue TRUE = new JsonValue() {
+        @Override
+        public ValueType getValueType() {
+            return ValueType.TRUE;
+        }
+
+        @Override
+        public boolean equals(final Object obj) {
+            return obj instanceof JsonValue && getValueType().equals(JsonValue.class.cast(obj).getValueType());
+        }
+
+        @Override
+        public int hashCode() {
+            return ValueType.TRUE.hashCode();
+        }
+
+        @Override
+        public String toString() {
+            return Boolean.TRUE.toString();
+        }
+
+        public JsonObject asJsonObject() {
+            return JsonObject.class.cast(this);
+        }
+
+        public JsonArray asJsonArray() {
+            return JsonArray.class.cast(this);
+        }
+    };
+
+    /**
+     * A constant JsonValue for FALSE
+     */
+    JsonValue FALSE = new JsonValue() {
+        @Override
+        public ValueType getValueType() {
+            return ValueType.FALSE;
+        }
+
+        @Override
+        public boolean equals(final Object obj) {
+            return obj instanceof JsonValue && getValueType().equals(JsonValue.class.cast(obj).getValueType());
+        }
+
+        @Override
+        public int hashCode() {
+            return ValueType.FALSE.hashCode();
+        }
+
+        @Override
+        public String toString() {
+            return Boolean.FALSE.toString();
+        }
+
+        public JsonObject asJsonObject() {
+            return JsonObject.class.cast(this);
+        }
+
+        public JsonArray asJsonArray() {
+            return JsonArray.class.cast(this);
+        }
+    };
+
+    ValueType getValueType();
+
+    @Override
+    String toString();
+
+    enum ValueType {
+        ARRAY,
+        OBJECT, STRING, NUMBER,
+        TRUE, FALSE,
+        NULL
+    }
+
+    JsonObject asJsonObject();
+
+    JsonArray asJsonArray();
+}
diff --git a/src/main/java/javax/json/JsonWriter.java b/src/main/java/javax/json/JsonWriter.java
new file mode 100644
index 0000000..da2756a
--- /dev/null
+++ b/src/main/java/javax/json/JsonWriter.java
@@ -0,0 +1,39 @@
+/*
+ * 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 javax.json;
+
+import java.io.Closeable;
+
+/**
+ * Write a JsonObject, JsonArray JsonStructure or more generic
+ * a JsonValue to the output.
+ *
+ * @see JsonWriterFactory
+ */
+public interface JsonWriter extends Closeable {
+
+    void writeArray(JsonArray array);
+
+    void writeObject(JsonObject object);
+
+    void write(JsonStructure value);
+
+    void write(JsonValue value);
+
+    @Override
+    void close();
+}
diff --git a/src/main/java/javax/json/JsonWriterFactory.java b/src/main/java/javax/json/JsonWriterFactory.java
new file mode 100644
index 0000000..e292947
--- /dev/null
+++ b/src/main/java/javax/json/JsonWriterFactory.java
@@ -0,0 +1,32 @@
+/*
+ * 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 javax.json;
+
+import java.io.OutputStream;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.util.Map;
+
+public interface JsonWriterFactory {
+    JsonWriter createWriter(Writer writer);
+
+    JsonWriter createWriter(OutputStream out);
+
+    JsonWriter createWriter(OutputStream out, Charset charset);
+
+    Map<String, ?> getConfigInUse();
+}
diff --git a/src/main/java/javax/json/spi/JsonProvider.java b/src/main/java/javax/json/spi/JsonProvider.java
index 5464ce6..63b2809 100644
--- a/src/main/java/javax/json/spi/JsonProvider.java
+++ b/src/main/java/javax/json/spi/JsonProvider.java
@@ -22,13 +22,26 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.Reader;
 import java.io.Writer;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Collection;
 import java.util.Map;
 
+import javax.json.JsonArray;
 import javax.json.JsonArrayBuilder;
 import javax.json.JsonBuilderFactory;
+import javax.json.JsonMergePatch;
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
 import javax.json.JsonObjectBuilder;
+import javax.json.JsonPatch;
+import javax.json.JsonPatchBuilder;
+import javax.json.JsonPointer;
 import javax.json.JsonReader;
 import javax.json.JsonReaderFactory;
+import javax.json.JsonString;
+import javax.json.JsonStructure;
+import javax.json.JsonValue;
 import javax.json.JsonWriter;
 import javax.json.JsonWriterFactory;
 import javax.json.stream.JsonGenerator;
@@ -51,32 +64,100 @@ public abstract class JsonProvider {
 
     public abstract JsonParser createParser(Reader reader);
 
-    public abstract JsonParser createParser(InputStream in);
+    public abstract JsonParser createParser(InputStream var1);
 
-    public abstract JsonParserFactory createParserFactory(Map<String, ?> config);
+    public abstract JsonParserFactory createParserFactory(Map<String, ?> var1);
 
-    public abstract JsonGenerator createGenerator(Writer writer);
+    public abstract JsonGenerator createGenerator(Writer var1);
 
-    public abstract JsonGenerator createGenerator(OutputStream out);
+    public abstract JsonGenerator createGenerator(OutputStream var1);
 
-    public abstract JsonGeneratorFactory createGeneratorFactory(Map<String, ?> config);
+    public abstract JsonGeneratorFactory createGeneratorFactory(Map<String, ?> var1);
 
-    public abstract JsonReader createReader(Reader reader);
+    public abstract JsonReader createReader(Reader var1);
 
-    public abstract JsonReader createReader(InputStream in);
+    public abstract JsonReader createReader(InputStream var1);
 
-    public abstract JsonWriter createWriter(Writer writer);
+    public abstract JsonWriter createWriter(Writer var1);
 
-    public abstract JsonWriter createWriter(OutputStream out);
+    public abstract JsonWriter createWriter(OutputStream var1);
 
-    public abstract JsonWriterFactory createWriterFactory(Map<String, ?> config);
+    public abstract JsonWriterFactory createWriterFactory(Map<String, ?> var1);
 
-    public abstract JsonReaderFactory createReaderFactory(Map<String, ?> config);
+    public abstract JsonReaderFactory createReaderFactory(Map<String, ?> var1);
 
     public abstract JsonObjectBuilder createObjectBuilder();
 
+    public JsonObjectBuilder createObjectBuilder(JsonObject jsonObject) {
+        throw new UnsupportedOperationException();
+    }
+
+    public JsonObjectBuilder createObjectBuilder(Map<String, Object> map) {
+        throw new UnsupportedOperationException();
+    }
+
     public abstract JsonArrayBuilder createArrayBuilder();
 
-    public abstract JsonBuilderFactory createBuilderFactory(Map<String, ?> config);
+    public JsonArrayBuilder createArrayBuilder(JsonArray initialData) {
+        throw new UnsupportedOperationException();
+    }
+
+    public JsonArrayBuilder createArrayBuilder(Collection<?> initialData) {
+        throw new UnsupportedOperationException();
+    }
+
+    public JsonPointer createPointer(String path) {
+        throw new UnsupportedOperationException();
+    }
+
+    public abstract JsonBuilderFactory createBuilderFactory(Map<String, ?> var1);
+
+    public JsonString createValue(String value) {
+        throw new UnsupportedOperationException();
+    }
+
+    public JsonNumber createValue(int value) {
+        throw new UnsupportedOperationException();
+    }
+
+    public JsonNumber createValue(long value) {
+        throw new UnsupportedOperationException();
+    }
+
+    public JsonNumber createValue(double value) {
+        throw new UnsupportedOperationException();
+    }
+
+    public JsonNumber createValue(BigDecimal value) {
+        throw new UnsupportedOperationException();
+    }
+
+    public JsonNumber createValue(BigInteger value) {
+        throw new UnsupportedOperationException();
+    }
+
+    public JsonPatch createPatch(JsonArray array) {
+        throw new UnsupportedOperationException();
+    }
+
+    public JsonPatch createDiff(JsonStructure source, JsonStructure target) {
+        throw new UnsupportedOperationException();
+    }
+
+    public JsonPatchBuilder createPatchBuilder() {
+        throw new UnsupportedOperationException();
+    }
+
+    public JsonPatchBuilder createPatchBuilder(JsonArray initialData) {
+        throw new UnsupportedOperationException();
+    }
+
+    public JsonMergePatch createMergePatch(JsonValue patch) {
+        throw new UnsupportedOperationException();
+    }
+
+    public JsonMergePatch createMergeDiff(JsonValue source, JsonValue target) {
+        throw new UnsupportedOperationException();
+    }
 }
 
diff --git a/src/main/java/javax/json/stream/JsonGenerationException.java b/src/main/java/javax/json/stream/JsonGenerationException.java
new file mode 100644
index 0000000..46e12d8
--- /dev/null
+++ b/src/main/java/javax/json/stream/JsonGenerationException.java
@@ -0,0 +1,32 @@
+/*
+ * 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 javax.json.stream;
+
+import javax.json.JsonException;
+
+public class JsonGenerationException extends JsonException {
+    public JsonGenerationException(final String message) {
+        super(message);
+    }
+
+    public JsonGenerationException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+
+}
+
+
diff --git a/src/main/java/javax/json/stream/JsonGenerator.java b/src/main/java/javax/json/stream/JsonGenerator.java
new file mode 100644
index 0000000..de9587a
--- /dev/null
+++ b/src/main/java/javax/json/stream/JsonGenerator.java
@@ -0,0 +1,88 @@
+/*
+ * 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 javax.json.stream;
+
+import javax.json.JsonValue;
+import java.io.Closeable;
+import java.io.Flushable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+public interface JsonGenerator extends Flushable, Closeable {
+    String PRETTY_PRINTING = "javax.json.stream.JsonGenerator.prettyPrinting"; // TODO: ensure it exists before releasing
+
+    JsonGenerator writeStartObject();
+
+    JsonGenerator writeStartObject(String name);
+
+    JsonGenerator writeStartArray();
+
+    JsonGenerator writeStartArray(String name);
+
+    /**
+     * Write the key with a colon;
+     *
+     * @throws JsonGenerationException if this method is not called within an object context
+     * @since 1.1
+     */
+    JsonGenerator writeKey(String name);
+
+    JsonGenerator write(String name, JsonValue value);
+
+    JsonGenerator write(String name, String value);
+
+    JsonGenerator write(String name, BigInteger value);
+
+    JsonGenerator write(String name, BigDecimal value);
+
+    JsonGenerator write(String name, int value);
+
+    JsonGenerator write(String name, long value);
+
+    JsonGenerator write(String name, double value);
+
+    JsonGenerator write(String name, boolean value);
+
+    JsonGenerator writeNull(String name);
+
+    JsonGenerator writeEnd();
+
+    JsonGenerator write(JsonValue value);
+
+    JsonGenerator write(String value);
+
+    JsonGenerator write(BigDecimal value);
+
+    JsonGenerator write(BigInteger value);
+
+    JsonGenerator write(int value);
+
+    JsonGenerator write(long value);
+
+    JsonGenerator write(double value);
+
+    JsonGenerator write(boolean value);
+
+    JsonGenerator writeNull();
+
+    @Override
+    void close();
+
+    @Override
+    void flush();
+
+}
diff --git a/src/main/java/javax/json/stream/JsonGeneratorFactory.java b/src/main/java/javax/json/stream/JsonGeneratorFactory.java
new file mode 100644
index 0000000..fbfc6c2
--- /dev/null
+++ b/src/main/java/javax/json/stream/JsonGeneratorFactory.java
@@ -0,0 +1,32 @@
+/*
+ * 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 javax.json.stream;
+
+import java.io.OutputStream;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.util.Map;
+
+public interface JsonGeneratorFactory {
+    JsonGenerator createGenerator(Writer writer);
+
+    JsonGenerator createGenerator(OutputStream out);
+
+    JsonGenerator createGenerator(OutputStream out, Charset charset);
+
+    Map<String, ?> getConfigInUse();
+}
diff --git a/src/main/java/javax/json/stream/JsonLocation.java b/src/main/java/javax/json/stream/JsonLocation.java
new file mode 100644
index 0000000..0fc1501
--- /dev/null
+++ b/src/main/java/javax/json/stream/JsonLocation.java
@@ -0,0 +1,26 @@
+/*
+ * 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 javax.json.stream;
+
+public interface JsonLocation {
+    long getLineNumber();
+
+    long getColumnNumber();
+
+    long getStreamOffset();
+}
+
diff --git a/src/main/java/javax/json/stream/JsonParser.java b/src/main/java/javax/json/stream/JsonParser.java
new file mode 100644
index 0000000..cbfc720
--- /dev/null
+++ b/src/main/java/javax/json/stream/JsonParser.java
@@ -0,0 +1,71 @@
+/*
+ * 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 javax.json.stream;
+
+import java.io.Closeable;
+import java.math.BigDecimal;
+import java.util.Map;
+import java.util.stream.Stream;
+
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import javax.json.JsonValue;
+
+public interface JsonParser extends Closeable {
+    boolean hasNext();
+
+    Event next();
+
+    String getString();
+
+    boolean isIntegralNumber();
+
+    int getInt();
+
+    long getLong();
+
+    BigDecimal getBigDecimal();
+
+    JsonLocation getLocation();
+
+    @Override
+    void close();
+
+    enum Event {
+        START_ARRAY, START_OBJECT,
+        KEY_NAME,
+        VALUE_STRING, VALUE_NUMBER, VALUE_TRUE, VALUE_FALSE, VALUE_NULL,
+        END_OBJECT, END_ARRAY
+    }
+    
+    JsonObject getObject();
+
+    JsonValue getValue();
+
+    JsonArray getArray();
+
+    Stream<JsonValue> getArrayStream();
+
+    Stream<Map.Entry<String,JsonValue>> getObjectStream();
+
+    Stream<JsonValue> getValueStream();
+
+    void skipArray();
+
+    void skipObject();
+}
+
diff --git a/src/main/java/javax/json/stream/JsonParserFactory.java b/src/main/java/javax/json/stream/JsonParserFactory.java
new file mode 100644
index 0000000..aa28fb7
--- /dev/null
+++ b/src/main/java/javax/json/stream/JsonParserFactory.java
@@ -0,0 +1,39 @@
+/*
+ * 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 javax.json.stream;
+
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import java.io.InputStream;
+import java.io.Reader;
+import java.nio.charset.Charset;
+import java.util.Map;
+
+public interface JsonParserFactory {
+    JsonParser createParser(Reader reader);
+
+    JsonParser createParser(InputStream in);
+
+    JsonParser createParser(InputStream in, Charset charset);
+
+    JsonParser createParser(JsonObject obj);
+
+    JsonParser createParser(JsonArray array);
+
+    Map<String, ?> getConfigInUse();
+}
+
diff --git a/src/main/java/javax/json/stream/JsonParsingException.java b/src/main/java/javax/json/stream/JsonParsingException.java
new file mode 100644
index 0000000..757a8bc
--- /dev/null
+++ b/src/main/java/javax/json/stream/JsonParsingException.java
@@ -0,0 +1,39 @@
+/*
+ * 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 javax.json.stream;
+
+import javax.json.JsonException;
+
+public class JsonParsingException extends JsonException {
+    private final JsonLocation location;
+
+    public JsonParsingException(String message, JsonLocation location) {
+        super(message);
+        this.location = location;
+    }
+
+    public JsonParsingException(String message, Throwable cause, JsonLocation location) {
+        super(message, cause);
+        this.location = location;
+    }
+
+    public JsonLocation getLocation() {
+        return location;
+    }
+}
+
+
diff --git a/src/main/java/org/apache/johnzon/core/AbstractJsonFactory.java b/src/main/java/org/apache/johnzon/core/AbstractJsonFactory.java
new file mode 100644
index 0000000..4ab42d5
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/AbstractJsonFactory.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.logging.Logger;
+
+public abstract class AbstractJsonFactory implements Serializable{
+
+    protected final Logger logger = Logger.getLogger(this.getClass().getName());
+    
+    public static final String BUFFER_STRATEGY = "org.apache.johnzon.buffer-strategy";
+    public static final BufferStrategy DEFAULT_BUFFER_STRATEGY = BufferStrategy.QUEUE;
+    
+    protected final Map<String, Object> internalConfig = new HashMap<String, Object>();
+    
+    protected AbstractJsonFactory(final Map<String, ?> config, Collection<String> supportedConfigKeys, Collection<String> defaultSupportedConfigKeys) {
+        if(config != null && config.size() > 0) {
+            
+            if(defaultSupportedConfigKeys != null) {
+                supportedConfigKeys = new ArrayList<String>(supportedConfigKeys);
+                supportedConfigKeys.addAll(defaultSupportedConfigKeys);
+            }
+            
+            for (String configKey : config.keySet()) {
+                if(supportedConfigKeys.contains(configKey)) {
+                    internalConfig.put(configKey, config.get(configKey));
+                } else {
+                    logger.warning(configKey + " is not supported by " + getClass().getName());
+                }
+            }
+        }
+    }
+
+    protected BufferStrategy getBufferProvider() {
+        final Object name = internalConfig.get(BUFFER_STRATEGY);
+        if (name != null) {
+            return BufferStrategy.valueOf(name.toString().toUpperCase(Locale.ENGLISH));
+        }
+        return DEFAULT_BUFFER_STRATEGY;
+    }
+
+    protected int getInt(final String key, final int defaultValue) {
+        final Object intValue = internalConfig.get(key);
+        if (intValue == null) {
+            return defaultValue;
+        } else if (Number.class.isInstance(intValue)) {
+            return Number.class.cast(intValue).intValue();
+        }
+        return Integer.parseInt(intValue.toString());
+    }
+
+    protected boolean getBool(final String key, final boolean defaultValue) {
+        final Object boolValue = internalConfig.get(key);
+        if (boolValue == null) {
+            return defaultValue;
+        } else if (Boolean.class.isInstance(boolValue)) {
+            return Boolean.class.cast(boolValue);
+        }
+        return Boolean.parseBoolean(boolValue.toString());
+    }
+
+}
diff --git a/src/main/java/org/apache/johnzon/core/BufferStrategy.java b/src/main/java/org/apache/johnzon/core/BufferStrategy.java
new file mode 100644
index 0000000..c37ecfb
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/BufferStrategy.java
@@ -0,0 +1,272 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import java.io.Serializable;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+public enum BufferStrategy {
+    BY_INSTANCE {
+        @Override
+        public BufferProvider<char[]> newCharProvider(final int size) {
+            return new CharBufferByInstanceProvider(size);
+        }
+
+        @Override
+        public BufferProvider<StringBuilder> newStringBuilderProvider(final int size) {
+            return new StringBuilderByInstanceProvider(size);
+        }
+    },
+    THREAD_LOCAL {
+        @Override
+        public BufferProvider<char[]> newCharProvider(final int size) {
+            return new CharBufferThreadLocalProvider(size);
+        }
+
+        @Override
+        public BufferProvider<StringBuilder> newStringBuilderProvider(final int size) {
+            return new StringBuilderThreadLocalProvider(size);
+        }
+    },
+    QUEUE {
+        @Override
+        public BufferProvider<char[]> newCharProvider(final int size) {
+            return new CharBufferQueueProvider(size);
+        }
+
+        @Override
+        public BufferProvider<StringBuilder> newStringBuilderProvider(final int size) {
+            return new StringBuilderQueueProvider(size);
+        }
+    },
+    SINGLETON {
+        @Override
+        public BufferProvider<char[]> newCharProvider(final int size) {
+            return new CharBufferSingletonProvider(size);
+        }
+
+        @Override
+        public BufferProvider<StringBuilder> newStringBuilderProvider(final int size) {
+            return new StringBuilderSingletonProvider(size);
+        }
+    };
+
+    public abstract BufferProvider<char[]> newCharProvider(int size);
+    public abstract BufferProvider<StringBuilder> newStringBuilderProvider(int size);
+
+    public static interface BufferProvider<T> extends Serializable {
+        T newBuffer();
+
+        void release(T value);
+    }
+
+    private static class CharBufferSingletonProvider extends SingletonProvider<char[]> {
+        public CharBufferSingletonProvider(final int size) {
+            super(size);
+        }
+
+        @Override
+        protected char[] newInstance(int size) {
+            return new char[size];
+        }
+
+        @Override
+        public void release(final char[] value) {
+            // no-op
+        }
+    }
+
+    private static class StringBuilderSingletonProvider extends SingletonProvider<StringBuilder> {
+        public StringBuilderSingletonProvider(final int size) {
+            super(size);
+        }
+
+        @Override
+        protected StringBuilder newInstance(final int size) {
+            return new StringBuilder(size);
+        }
+
+        @Override
+        public void release(final StringBuilder value) {
+            value.setLength(0);
+        }
+    }
+
+    private static abstract class SingletonProvider<T> implements BufferProvider<T> {
+        protected final T buffer;
+
+        public SingletonProvider(final int size) {
+            buffer = newInstance(size);
+        }
+
+        protected abstract T newInstance(int size);
+
+        @Override
+        public T newBuffer() {
+            return buffer;
+        }
+
+        @Override
+        public void release(final T value) {
+            // no-op
+        }
+    }
+
+    private static abstract class ThreadLocalProvider<T> implements BufferProvider<T> {
+        private final ThreadLocalBufferCache<T> cache;
+
+        public ThreadLocalProvider(final int size) {
+            cache = new ThreadLocalBufferCache<T>(size) {
+                @Override
+                protected T newValue(int defaultSize) {
+                    return newInstance(size);
+                }
+            };
+        }
+
+        protected abstract T newInstance(int size);
+
+        @Override
+        public T newBuffer() {
+            return cache.getCache();
+        }
+
+        @Override
+        public void release(final T value) {
+            cache.release(value);
+        }
+    }
+
+    private static class CharBufferThreadLocalProvider extends ThreadLocalProvider<char[]> {
+        public CharBufferThreadLocalProvider(int size) {
+            super(size);
+        }
+
+        @Override
+        protected char[] newInstance(final int size) {
+            return new char[size];
+        }
+    }
+
+    private static class StringBuilderThreadLocalProvider extends ThreadLocalProvider<StringBuilder> {
+        public StringBuilderThreadLocalProvider(int size) {
+            super(size);
+        }
+
+        @Override
+        protected StringBuilder newInstance(final int size) {
+            return new StringBuilder(size);
+        }
+
+        @Override
+        public void release(final StringBuilder value) {
+            value.setLength(0);
+            super.release(value);
+        }
+    }
+
+    private static class CharBufferByInstanceProvider implements BufferProvider<char[]> {
+        private final int size;
+
+        public CharBufferByInstanceProvider(final int size) {
+            this.size = size;
+        }
+
+        @Override
+        public char[] newBuffer() {
+            return new char[size];
+        }
+
+        @Override
+        public void release(final char[] value) {
+            // no-op
+        }
+    }
+
+    private static class StringBuilderByInstanceProvider implements BufferProvider<StringBuilder> {
+        private final int size;
+
+        public StringBuilderByInstanceProvider(final int size) {
+            this.size = size;
+        }
+
+        @Override
+        public StringBuilder newBuffer() {
+            return new StringBuilder(size);
+        }
+
+        @Override
+        public void release(final StringBuilder value) {
+            // no-op
+        }
+    }
+
+    private static abstract class QueueProvider<T> implements BufferProvider<T> {
+        private final int size;
+        private final ConcurrentLinkedQueue<T> queue = new ConcurrentLinkedQueue<T>();
+
+        public QueueProvider(final int size) {
+            this.size = size;
+        }
+
+        protected abstract T newInstance(int size);
+
+        @Override
+        public T newBuffer() {
+            final T buffer = queue.poll();
+            if (buffer == null) {
+                return newInstance(size);
+            }
+            return buffer;
+        }
+
+        @Override
+        public void release(final T value) {
+            queue.offer(value);
+        }
+    }
+
+    private static class CharBufferQueueProvider extends QueueProvider<char[]> {
+        public CharBufferQueueProvider(final int size) {
+            super(size);
+        }
+
+        @Override
+        protected char[] newInstance(int size) {
+            return new char[size];
+        }
+    }
+
+    private static class StringBuilderQueueProvider extends QueueProvider<StringBuilder> {
+        public StringBuilderQueueProvider(final int size) {
+            super(size);
+        }
+
+        @Override
+        protected StringBuilder newInstance(int size) {
+            return new StringBuilder(size);
+        }
+
+        @Override
+        public void release(final StringBuilder value) {
+            value.setLength(0);
+            super.release(value);
+        }
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/CommentsJsonStreamParserImpl.java b/src/main/java/org/apache/johnzon/core/CommentsJsonStreamParserImpl.java
new file mode 100644
index 0000000..f9d298e
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/CommentsJsonStreamParserImpl.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.nio.charset.Charset;
+
+public class CommentsJsonStreamParserImpl extends JsonStreamParserImpl {
+    public CommentsJsonStreamParserImpl(final InputStream inputStream,
+                                        final int maxStringLength,
+                                        final BufferStrategy.BufferProvider<char[]> bufferProvider,
+                                        final BufferStrategy.BufferProvider<char[]> valueBuffer,
+                                        final boolean autoAdjust) {
+        super(inputStream, maxStringLength, bufferProvider, valueBuffer, autoAdjust);
+    }
+
+    public CommentsJsonStreamParserImpl(final InputStream inputStream,
+                                        final Charset encoding,
+                                        final int maxStringLength,
+                                        final BufferStrategy.BufferProvider<char[]> bufferProvider,
+                                        final BufferStrategy.BufferProvider<char[]> valueBuffer,
+                                        final boolean autoAdjust) {
+        super(inputStream, encoding, maxStringLength, bufferProvider, valueBuffer, autoAdjust);
+    }
+
+    public CommentsJsonStreamParserImpl(final Reader reader,
+                                        final int maxStringLength,
+                                        final BufferStrategy.BufferProvider<char[]> bufferProvider,
+                                        final BufferStrategy.BufferProvider<char[]> valueBuffer,
+                                        final boolean autoAdjust) {
+        super(reader, maxStringLength, bufferProvider, valueBuffer, autoAdjust);
+    }
+
+    @Override
+    protected Event defaultHandling(final char c) {
+        if (c == '/') {
+            char next = readNextChar();
+            if (next == '/') { // fail
+                do {
+                    next = readNextChar();
+                } while (next != EOL);
+                return next();
+            } else if (next == '*') {
+                next = 0;
+                char previous;
+                do {
+                    previous = next;
+                    next = readNextNonWhitespaceChar(readNextChar());
+                } while (next != '/' || previous != '*');
+                readNextNonWhitespaceChar(next);
+                return next();
+            }
+        }
+        return super.defaultHandling(c);
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/DiffBase.java b/src/main/java/org/apache/johnzon/core/DiffBase.java
new file mode 100644
index 0000000..d6abfdf
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/DiffBase.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.johnzon.core;
+
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import javax.json.JsonValue;
+
+/**
+ * Commonly used methods for diffs
+ */
+class DiffBase {
+    protected boolean isJsonObject(JsonValue jsonValue) {
+        return jsonValue instanceof JsonObject;
+    }
+
+    protected boolean isJsonArray(JsonValue targetValue) {
+        return targetValue instanceof JsonArray;
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/Experimental.java b/src/main/java/org/apache/johnzon/core/Experimental.java
new file mode 100644
index 0000000..b645c99
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/Experimental.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Marker for experimental API.
+ */
+@Target(TYPE)
+@Retention(RUNTIME)
+public @interface Experimental {
+}
diff --git a/src/main/java/org/apache/johnzon/core/HStack.java b/src/main/java/org/apache/johnzon/core/HStack.java
new file mode 100644
index 0000000..b2d57bd
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/HStack.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import java.io.Serializable;
+
+public class HStack<T> implements Serializable {
+
+    private Node<T> topElement = null;
+    private int size;
+
+
+    private static final class Node<T> implements Serializable {
+        private final Node<T> previous;
+        private final T object;
+
+        private Node(final Node<T> previous, final T object) {
+            super();
+            this.previous = previous;
+            this.object = object;
+        }
+    }
+
+
+    void push(T object) {
+        topElement = new Node<T>(topElement, object);
+        size++;
+    }
+
+    T pop() {
+
+        if (topElement == null) {
+            return null;
+        }
+
+        T tmp = topElement.object;
+        topElement = topElement.previous;
+        size--;
+        return tmp;
+    }
+
+    T peek() {
+        return topElement == null ? null : topElement.object;
+    }
+
+    int size() {
+        return size;
+    }
+
+}
diff --git a/src/main/java/org/apache/johnzon/core/JohnzonJsonParser.java b/src/main/java/org/apache/johnzon/core/JohnzonJsonParser.java
new file mode 100644
index 0000000..b13852b
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JohnzonJsonParser.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.johnzon.core;
+
+import java.math.BigDecimal;
+import java.util.Map;
+import java.util.stream.Stream;
+
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import javax.json.JsonValue;
+import javax.json.stream.JsonLocation;
+import javax.json.stream.JsonParser;
+
+/**
+ * JsonParser with extended functionality
+ */
+public interface JohnzonJsonParser extends JsonParser {
+
+    boolean isNotTooLong();
+
+
+    public static class JohnzonJsonParserWrapper implements JohnzonJsonParser {
+        private final JsonParser jsonParser;
+
+        public JohnzonJsonParserWrapper(JsonParser jsonParser) {
+            this.jsonParser = jsonParser;
+        }
+
+        @Override
+        public boolean isNotTooLong() {
+            return true;
+        }
+
+        @Override
+        public boolean hasNext() {
+            return jsonParser.hasNext();
+        }
+
+        @Override
+        public Event next() {
+            return jsonParser.next();
+        }
+
+        @Override
+        public String getString() {
+            return jsonParser.getString();
+        }
+
+        @Override
+        public boolean isIntegralNumber() {
+            return jsonParser.isIntegralNumber();
+        }
+
+        @Override
+        public int getInt() {
+            return jsonParser.getInt();
+        }
+
+        @Override
+        public long getLong() {
+            return jsonParser.getLong();
+        }
+
+        @Override
+        public BigDecimal getBigDecimal() {
+            return jsonParser.getBigDecimal();
+        }
+
+        @Override
+        public JsonLocation getLocation() {
+            return jsonParser.getLocation();
+        }
+
+        @Override
+        public void close() {
+            jsonParser.close();
+        }
+
+        @Override
+        public JsonObject getObject() {
+            return jsonParser.getObject();
+        }
+
+        @Override
+        public JsonValue getValue() {
+            return jsonParser.getValue();
+        }
+
+        @Override
+        public JsonArray getArray() {
+            return jsonParser.getArray();
+        }
+
+        @Override
+        public Stream<JsonValue> getArrayStream() {
+            return jsonParser.getArrayStream();
+        }
+
+        @Override
+        public Stream<Map.Entry<String, JsonValue>> getObjectStream() {
+            return jsonParser.getObjectStream();
+        }
+
+        @Override
+        public Stream<JsonValue> getValueStream() {
+            return jsonParser.getValueStream();
+        }
+
+        @Override
+        public void skipArray() {
+            jsonParser.skipArray();
+        }
+
+        @Override
+        public void skipObject() {
+            jsonParser.skipObject();
+        }
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonArrayBuilderImpl.java b/src/main/java/org/apache/johnzon/core/JsonArrayBuilderImpl.java
new file mode 100644
index 0000000..7790288
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonArrayBuilderImpl.java
@@ -0,0 +1,330 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import javax.json.JsonArray;
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonException;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonValue;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+class JsonArrayBuilderImpl implements JsonArrayBuilder, Serializable {
+    private List<JsonValue> tmpList;
+
+    public JsonArrayBuilderImpl() {
+    }
+
+    public JsonArrayBuilderImpl(JsonArray initialData) {
+        tmpList = new ArrayList<>(initialData);
+    }
+
+    public JsonArrayBuilderImpl(Collection<?> initialData) {
+        tmpList = new ArrayList<>();
+        for (Object initialValue : initialData) {
+            add(initialValue);
+        }
+    }
+
+    @Override
+    public JsonArrayBuilder addAll(final JsonArrayBuilder builder) {
+        for (JsonValue value : builder.build())
+        {
+            add(value);
+        }
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final int index, final JsonValue value) {
+        addValue(index, value);
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final int index, final String value) {
+        addValue(index, new JsonStringImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final int index, final BigDecimal value) {
+        addValue(index, new JsonNumberImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final int index, final BigInteger value) {
+        addValue(index, new JsonNumberImpl(new BigDecimal(value)));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final int index, final int value) {
+        addValue(index, new JsonLongImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final int index, final long value) {
+        addValue(index, new JsonLongImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final int index, final double value) {
+        addValue(index, new JsonDoubleImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final int index, final boolean value) {
+        addValue(index, value ? JsonValue.TRUE : JsonValue.FALSE);
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder addNull(final int index) {
+        addValue(index, JsonValue.NULL);
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final int index, final JsonObjectBuilder builder) {
+        addValue(index, builder.build());
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final int index, final JsonArrayBuilder builder) {
+        addValue(index, builder.build());
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder set(final int index, final JsonValue value) {
+        setValue(index, value);
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder set(final int index, final String value) {
+        setValue(index, new JsonStringImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder set(final int index, final BigDecimal value) {
+        setValue(index, new JsonNumberImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder set(final int index, final BigInteger value) {
+        setValue(index, new JsonNumberImpl(new BigDecimal(value)));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder set(final int index, final int value) {
+        setValue(index, new JsonLongImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder set(final int index, final long value) {
+        setValue(index, new JsonLongImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder set(final int index, final double value) {
+        setValue(index, new JsonDoubleImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder set(final int index, final boolean value) {
+        setValue(index, value ? JsonValue.TRUE : JsonValue.FALSE);
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder setNull(final int index) {
+        setValue(index, JsonValue.NULL);
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder set(final int index, final JsonObjectBuilder builder) {
+        setValue(index, builder.build());
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder set(final int index, final JsonArrayBuilder builder) {
+        setValue(index, builder.build());
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder remove(final int index) {
+        tmpList.remove(index);
+        return this;
+    }
+
+    public JsonArrayBuilder add(final Object value) {
+        if (value instanceof JsonValue) {
+            add((JsonValue) value);
+        } else if (value instanceof BigDecimal) {
+            add((BigDecimal) value);
+        } else if (value instanceof BigInteger) {
+            add((BigInteger) value);
+        } else if (value instanceof Boolean) {
+            add((boolean) value);
+        } else if (value instanceof Double) {
+            add((double) value);
+        } else if (value instanceof Integer) {
+            add((int) value);
+        } else if (value instanceof Long) {
+            add((long) value);
+        } else if (value instanceof String) {
+            add((String) value);
+        } else {
+            throw new JsonException("Illegal JSON type! type=" + value.getClass());
+        }
+
+        return this;
+    }
+
+        @Override
+    public JsonArrayBuilder add(final JsonValue value) {
+        addValue(value);
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final String value) {
+        addValue(new JsonStringImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final BigDecimal value) {
+        addValue(new JsonNumberImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final BigInteger value) {
+        addValue(new JsonNumberImpl(new BigDecimal(value)));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final int value) {
+        addValue(new JsonLongImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final long value) {
+        addValue(new JsonLongImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final double value) {
+        addValue(new JsonDoubleImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final boolean value) {
+        addValue(value ? JsonValue.TRUE : JsonValue.FALSE);
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder addNull() {
+        addValue(JsonValue.NULL);
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final JsonObjectBuilder builder) {
+        addValue(builder.build());
+        return this;
+    }
+
+    @Override
+    public JsonArrayBuilder add(final JsonArrayBuilder builder) {
+        addValue(builder.build());
+        return this;
+    }
+    
+    private void setValue(int idx, JsonValue value) {
+        if (value == null || tmpList == null) {
+            throw npe();
+        }
+        tmpList.set(idx, value);
+    }
+
+    private void addValue(JsonValue value) {
+        if (value == null) {
+            throw npe();
+        }
+
+        if(tmpList==null){
+            tmpList=new ArrayList<>();
+        }
+
+        tmpList.add(value);
+    }
+
+    private void addValue(int idx, JsonValue value) {
+        if (value == null) {
+            throw npe();
+        }
+
+        if(tmpList==null){
+            tmpList=new ArrayList<>();
+        }
+
+        tmpList.add(idx, value);
+    }
+
+    @Override
+    public JsonArray build() {
+        if(tmpList == null) {
+            return new JsonArrayImpl(Collections.<JsonValue>emptyList());
+        }
+        return new JsonArrayImpl(Collections.unmodifiableList(tmpList));
+    }
+
+    private static NullPointerException npe() {
+        throw new NullPointerException("value/builder must not be null");
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonArrayImpl.java b/src/main/java/org/apache/johnzon/core/JsonArrayImpl.java
new file mode 100644
index 0000000..a8d85e4
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonArrayImpl.java
@@ -0,0 +1,235 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import javax.json.JsonArray;
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import javax.json.JsonString;
+import javax.json.JsonValue;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+class JsonArrayImpl extends AbstractList<JsonValue> implements JsonArray, Serializable {
+    private Integer hashCode = null;
+    private final List<JsonValue> unmodifieableBackingList;
+    private int size = -1;
+
+    JsonArrayImpl(final List<JsonValue> backingList) {
+        super();
+        this.unmodifieableBackingList = backingList;
+    }
+
+    private <T> T value(final int idx, final Class<T> type) {
+        if (idx > unmodifieableBackingList.size()) {
+            throw new IndexOutOfBoundsException(idx + "/" + unmodifieableBackingList.size());
+        }
+        return type.cast(unmodifieableBackingList.get(idx));
+    }
+
+    @Override
+    public JsonObject getJsonObject(final int index) {
+        return value(index, JsonObject.class);
+    }
+
+    @Override
+    public JsonArray getJsonArray(final int index) {
+        return value(index, JsonArray.class);
+    }
+
+    @Override
+    public JsonNumber getJsonNumber(final int index) {
+        return value(index, JsonNumber.class);
+    }
+
+    @Override
+    public JsonString getJsonString(final int index) {
+        return value(index, JsonString.class);
+    }
+
+    @Override
+    public <T extends JsonValue> List<T> getValuesAs(Class<T> clazz) {
+        return (List<T>) unmodifieableBackingList;
+    }
+
+    @Override
+    public <T, K extends JsonValue> List<T> getValuesAs(Function<K, T> func) {
+        List<T> result = new ArrayList<T>();
+        for (K value : (List<K>) this) {
+            result.add(func.apply(value));
+        }
+        return result;
+    }
+
+    @Override
+    public String getString(final int index) {
+        return value(index, JsonString.class).getString();
+    }
+
+    @Override
+    public String getString(final int index, final String defaultValue) {
+        JsonValue val = null;
+        int s = size;
+
+        if (s == -1) {
+            s = unmodifieableBackingList.size();
+            size = s;
+        }
+
+        if (index > s - 1 || !((val = get(index)) instanceof JsonString)) {
+            return defaultValue;
+        } else {
+            return JsonString.class.cast(val).getString();
+        }
+    }
+
+    @Override
+    public int getInt(final int index) {
+        return value(index, JsonNumber.class).intValue();
+    }
+
+    @Override
+    public int getInt(final int index, final int defaultValue) {
+        JsonValue val = null;
+        int s = size;
+
+        if (s == -1) {
+            s = unmodifieableBackingList.size();
+            size = s;
+        }
+
+        if (index > s - 1 || !((val = get(index)) instanceof JsonNumber)) {
+            return defaultValue;
+        } else {
+            return JsonNumber.class.cast(val).intValue();
+        }
+    }
+
+    @Override
+    public boolean getBoolean(final int index) {
+        final JsonValue val = value(index, JsonValue.class);
+
+        if (JsonValue.TRUE.equals(val)) {
+            return true;
+        } else if (JsonValue.FALSE.equals(val)) {
+            return false;
+        } else {
+            throw new ClassCastException();
+        }
+
+    }
+
+    @Override
+    public boolean getBoolean(final int index, final boolean defaultValue) {
+
+        int s = size;
+
+        if (s == -1) {
+            s = unmodifieableBackingList.size();
+            size = s;
+        }
+
+        if (index > s - 1) {
+            return defaultValue;
+        }
+
+        final JsonValue val = get(index);
+        return JsonValue.TRUE.equals(val) || !JsonValue.FALSE.equals(val) && defaultValue;
+    }
+
+    @Override
+    public boolean isNull(final int index) {
+        return JsonValue.NULL.equals(value(index, JsonValue.class));
+    }
+
+    @Override
+    public ValueType getValueType() {
+        return ValueType.ARRAY;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder builder = new StringBuilder("[");
+        final Iterator<JsonValue> it = unmodifieableBackingList.iterator();
+        boolean hasNext = it.hasNext();
+        while (hasNext) {
+            final JsonValue jsonValue = it.next();
+            if (JsonString.class.isInstance(jsonValue)) {
+                builder.append(jsonValue.toString());
+            } else {
+                builder.append(jsonValue != JsonValue.NULL ? jsonValue.toString() : JsonChars.NULL);
+            }
+            hasNext = it.hasNext();
+            if (hasNext) {
+                builder.append(",");
+            }
+        }
+        return builder.append(']').toString();
+    }
+
+    @Override
+    public JsonObject asJsonObject() {
+        return JsonObject.class.cast(this);
+    }
+
+    @Override
+    public JsonArray asJsonArray() {
+        return JsonArray.class.cast(this);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return JsonArray.class.isInstance(obj) && super.equals(obj);
+    }
+
+    @Override
+    public int hashCode() {
+        Integer h = hashCode;
+        if (h == null) {
+            h = unmodifieableBackingList.hashCode();
+            hashCode = h;
+        }
+        return h;
+    }
+
+    @Override
+    public JsonValue get(final int index) {
+        return unmodifieableBackingList.get(index);
+    }
+
+    @Override
+    public int size() {
+        return unmodifieableBackingList.size();
+    }
+
+    private Object writeReplace() throws ObjectStreamException {
+        return new SerializableValue(toString());
+    }
+
+    @Override
+    public JsonValue getValue(String jsonPointer) {
+        return null;
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonBuilderFactoryImpl.java b/src/main/java/org/apache/johnzon/core/JsonBuilderFactoryImpl.java
new file mode 100644
index 0000000..81719ff
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonBuilderFactoryImpl.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Logger;
+
+import javax.json.JsonArray;
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonBuilderFactory;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+
+class JsonBuilderFactoryImpl implements JsonBuilderFactory {
+    private final Map<String, Object> internalConfig = new HashMap<String, Object>();
+    private static final String[] SUPPORTED_CONFIG_KEYS = new String[] {
+    //nothing yet
+
+    };
+    protected final Logger logger = Logger.getLogger(this.getClass().getName());
+
+    JsonBuilderFactoryImpl(final Map<String, ?> config) {
+        if (config != null && config.size() > 0) {
+            final List<String> supportedConfigKeys = Arrays.asList(SUPPORTED_CONFIG_KEYS);
+            for (String configKey : config.keySet()) {
+                if(supportedConfigKeys.contains(configKey)) {
+                    internalConfig.put(configKey, config.get(configKey));
+                } else {
+                    logger.warning(configKey + " is not supported by " + getClass().getName());
+                }
+            }
+        }
+    }
+
+    @Override
+    public JsonObjectBuilder createObjectBuilder() {
+        return new JsonObjectBuilderImpl();
+    }
+
+    @Override
+    public JsonObjectBuilder createObjectBuilder(JsonObject initialData) {
+        return new JsonObjectBuilderImpl(initialData);
+    }
+
+    @Override
+    public JsonArrayBuilder createArrayBuilder() {
+        return new JsonArrayBuilderImpl();
+    }
+
+
+    @Override
+    public JsonArrayBuilder createArrayBuilder(JsonArray initialData) {
+        return new JsonArrayBuilderImpl(initialData);
+    }
+
+    @Override
+    public JsonArrayBuilder createArrayBuilder(Collection<?> initialData) {
+        return new JsonArrayBuilderImpl(initialData);
+    }
+
+    @Override
+    public Map<String, ?> getConfigInUse() {
+        return Collections.unmodifiableMap(internalConfig);
+    }
+
+    @Override
+    public JsonObjectBuilder createObjectBuilder(Map<String, Object> initialValues) {
+        return new JsonObjectBuilderImpl(initialValues);
+    }
+
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonChars.java b/src/main/java/org/apache/johnzon/core/JsonChars.java
new file mode 100644
index 0000000..1e36913
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonChars.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import javax.json.stream.JsonParser.Event;
+
+public interface JsonChars {
+    char EOF = Character.MIN_VALUE;
+
+    char START_OBJECT_CHAR = '{';
+    char END_OBJECT_CHAR = '}';
+    char START_ARRAY_CHAR = '[';
+    char END_ARRAY_CHAR = ']';
+    char QUOTE_CHAR = '"';
+    char COMMA_CHAR = ',';
+    char KEY_SEPARATOR = ':';
+    
+    char EOL = '\n';
+    char SPACE = ' ';
+    
+    char TRUE_T = 't';
+    char TRUE_R = 'r';
+    char TRUE_U = 'u';
+    char TRUE_E = 'e';
+    char FALSE_F = 'f';
+    char FALSE_A = 'a';
+    char FALSE_L = 'l';
+    char FALSE_S = 's';
+    char FALSE_E = 'e';
+    char NULL_N = 'n';
+    char NULL_U = 'u';
+    char NULL_L = 'l';
+ 
+    char ZERO = '0';
+    char NINE = '9';
+    char DOT = '.';
+    char MINUS = '-';
+    char PLUS = '+';
+    char EXP_LOWERCASE = 'e';
+    char EXP_UPPERCASE = 'E';
+    char ESCAPE_CHAR = '\\';
+    
+    char TAB = '\t';
+    char BACKSPACE = '\b';
+    char FORMFEED = '\f';
+    char CR = '\r';
+
+    String NULL = "null".intern();
+    
+    static final byte START_ARRAY = (byte) Event.START_ARRAY.ordinal();
+    static final byte START_OBJECT = (byte) Event.START_OBJECT.ordinal();
+    static final byte KEY_NAME=(byte) Event.KEY_NAME.ordinal();
+    static final byte VALUE_STRING=(byte) Event.VALUE_STRING.ordinal(); 
+    static final byte VALUE_NUMBER=(byte) Event.VALUE_NUMBER.ordinal(); 
+    static final byte VALUE_TRUE=(byte) Event.VALUE_TRUE.ordinal();
+    static final byte VALUE_FALSE=(byte) Event.VALUE_FALSE.ordinal(); 
+    static final byte VALUE_NULL=(byte) Event.VALUE_NULL.ordinal();
+    static final byte END_OBJECT=(byte) Event.END_OBJECT.ordinal();
+    static final byte END_ARRAY=(byte) Event.END_ARRAY.ordinal();
+    
+    static final byte COMMA_EVENT=Byte.MAX_VALUE;
+    static final byte KEY_SEPARATOR_EVENT=Byte.MIN_VALUE;
+    
+    static final Event[] EVT_MAP =Event.values();
+    
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonDoubleImpl.java b/src/main/java/org/apache/johnzon/core/JsonDoubleImpl.java
new file mode 100644
index 0000000..2e99c8a
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonDoubleImpl.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import javax.json.JsonArray;
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Objects;
+
+final class JsonDoubleImpl implements JsonNumber, Serializable {
+    private final double value;
+
+    private Integer hashCode = null;
+
+    JsonDoubleImpl(final double value) {
+        
+        if(Double.isInfinite(value) || Double.isNaN(value)) {
+            throw new NumberFormatException("double value must not be NaN or Infinite");
+        }
+        
+        this.value = value;
+    }
+
+    @Override
+    public Number numberValue() {
+        return value;
+    }
+
+    @Override
+    public boolean isIntegral() {
+        return false;
+    }
+
+    @Override
+    public int intValue() {
+        return (int) value;
+    }
+
+    @Override
+    public int intValueExact() {
+        checkFractionalPart();
+        return intValue();
+    }
+
+    @Override
+    public long longValue() {
+        return (long) value;
+    }
+
+    @Override
+    public long longValueExact() {
+        checkFractionalPart();
+        return (long) value;
+    }
+
+    @Override
+    public BigInteger bigIntegerValue() {
+        return new BigDecimal(toString()).toBigInteger();
+    }
+
+    @Override
+    public BigInteger bigIntegerValueExact() {
+        return new BigDecimal(toString()).toBigIntegerExact();
+    }
+
+    @Override
+    public double doubleValue() {
+        return value;
+    }
+
+    @Override
+    public BigDecimal bigDecimalValue() {
+        return new BigDecimal(toString());
+    }
+
+    @Override
+    public ValueType getValueType() {
+        return ValueType.NUMBER;
+    }
+
+    @Override
+    public String toString() {
+        return Double.toString(value);
+    }
+
+    @Override
+    public int hashCode() {
+        if (hashCode == null) {
+            hashCode = bigDecimalValue().hashCode();
+        }
+
+        return hashCode;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (JsonDoubleImpl.class.isInstance(obj)) {
+            return JsonDoubleImpl.class.cast(obj).value == value;
+        }
+        return JsonNumber.class.isInstance(obj) && Objects.equals(JsonNumber.class.cast(obj).bigDecimalValue(), bigDecimalValue());
+    }
+
+    private void checkFractionalPart() {
+        if ((value % 1) != 0) {
+            throw new ArithmeticException("Not an int/long, use other value readers");
+        }
+    }
+
+    @Override
+    public JsonObject asJsonObject() {
+        return JsonObject.class.cast(this);
+    }
+
+    @Override
+    public JsonArray asJsonArray() {
+        return JsonArray.class.cast(this);
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonGeneratorFactoryImpl.java b/src/main/java/org/apache/johnzon/core/JsonGeneratorFactoryImpl.java
new file mode 100644
index 0000000..92abbcd
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonGeneratorFactoryImpl.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import static java.util.Arrays.asList;
+
+import java.io.OutputStream;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.json.stream.JsonGenerator;
+import javax.json.stream.JsonGeneratorFactory;
+
+public class JsonGeneratorFactoryImpl extends AbstractJsonFactory implements JsonGeneratorFactory {    
+    public static final String GENERATOR_BUFFER_LENGTH = "org.apache.johnzon.default-char-buffer-generator";
+    public static final int DEFAULT_GENERATOR_BUFFER_LENGTH =  Integer.getInteger(GENERATOR_BUFFER_LENGTH, 64 * 1024); //64k
+   
+    static final Collection<String> SUPPORTED_CONFIG_KEYS = asList(
+        JsonGenerator.PRETTY_PRINTING, GENERATOR_BUFFER_LENGTH, BUFFER_STRATEGY
+    );
+    //key caching currently disabled
+    private final ConcurrentMap<String, String> cache = null;//new ConcurrentHashMap<String, String>();
+    private final boolean pretty;
+    private final BufferStrategy.BufferProvider<char[]> bufferProvider;
+
+    public JsonGeneratorFactoryImpl(final Map<String, ?> config) {
+        
+          super(config, SUPPORTED_CONFIG_KEYS, null); 
+          
+          this.pretty = getBool(JsonGenerator.PRETTY_PRINTING, false);
+          
+          final int bufferSize = getInt(GENERATOR_BUFFER_LENGTH, DEFAULT_GENERATOR_BUFFER_LENGTH);
+          if (bufferSize <= 0) {
+              throw new IllegalArgumentException("buffer length must be greater than zero");
+          }
+
+          this.bufferProvider = getBufferProvider().newCharProvider(bufferSize);
+    }
+
+    @Override
+    public JsonGenerator createGenerator(final Writer writer) {
+        return new JsonGeneratorImpl(writer, bufferProvider, cache, pretty);
+    }
+
+    @Override
+    public JsonGenerator createGenerator(final OutputStream out) {
+        return new JsonGeneratorImpl(out, bufferProvider, cache, pretty);
+    }
+
+    @Override
+    public JsonGenerator createGenerator(final OutputStream out, final Charset charset) {
+        return new JsonGeneratorImpl(out,charset, bufferProvider, cache, pretty);
+    }
+
+    @Override
+    public Map<String, ?> getConfigInUse() {
+        return Collections.unmodifiableMap(internalConfig);
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonGeneratorImpl.java b/src/main/java/org/apache/johnzon/core/JsonGeneratorImpl.java
new file mode 100644
index 0000000..bc17340
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonGeneratorImpl.java
@@ -0,0 +1,827 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import javax.json.JsonArray;
+import javax.json.JsonException;
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import javax.json.JsonString;
+import javax.json.JsonValue;
+import javax.json.stream.JsonGenerationException;
+import javax.json.stream.JsonGenerator;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Serializable;
+import java.io.Writer;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.nio.charset.Charset;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentMap;
+
+class JsonGeneratorImpl implements JsonGenerator, JsonChars, Serializable {
+    private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
+
+    private final transient Writer writer;
+    private final BufferStrategy.BufferProvider<char[]> bufferProvider;
+    private final char[] buffer;
+    private int bufferPos = 0;
+    private final boolean prettyPrint;
+    private static final String INDENT = "  ";
+    //private final ConcurrentMap<String, String> cache;
+    private int depth = 0;
+
+    private final HStack<GeneratorState> state = new HStack<GeneratorState>();
+
+    private enum GeneratorState {
+        INITIAL(false, true), START_OBJECT(true, false), IN_OBJECT(true, false), AFTER_KEY(false, true), START_ARRAY(false, true), IN_ARRAY(
+                false, true), END(false, false);
+
+        private final boolean acceptsKey;
+        private final boolean acceptsValue;
+
+        GeneratorState(final boolean acceptsKey, final boolean acceptsValue) {
+            this.acceptsKey = acceptsKey;
+            this.acceptsValue = acceptsValue;
+        }
+    }
+
+    JsonGeneratorImpl(final Writer writer, final BufferStrategy.BufferProvider<char[]> bufferProvider,
+            final ConcurrentMap<String, String> cache, final boolean prettyPrint) {
+        this.writer = writer;
+        //this.cache = cache;
+        this.buffer = bufferProvider.newBuffer();
+        this.bufferProvider = bufferProvider;
+        this.prettyPrint = prettyPrint;
+        state.push(GeneratorState.INITIAL);
+    }
+
+    JsonGeneratorImpl(final OutputStream out, final BufferStrategy.BufferProvider<char[]> bufferProvider,
+            final ConcurrentMap<String, String> cache, final boolean prettyPrint) {
+        this(new OutputStreamWriter(out, UTF8_CHARSET), bufferProvider, cache, prettyPrint);
+    }
+
+    JsonGeneratorImpl(final OutputStream out, final Charset encoding, final BufferStrategy.BufferProvider<char[]> bufferProvider,
+            final ConcurrentMap<String, String> cache, final boolean prettyPrint) {
+        this(new OutputStreamWriter(out, encoding), bufferProvider, cache, prettyPrint);
+    }
+
+    private void writeEol() {
+        if (prettyPrint) {
+            justWrite(EOL);
+        }
+    }
+
+    private void writeIndent() {
+        if (prettyPrint && depth > 0) {
+            for (int i = 0; i < depth; i++) {
+                justWrite(INDENT);
+            }
+        }
+    }
+
+    //caching currently disabled
+    //two problems:
+    // 1) not easy to get the escaped value efficiently when its streamed and the buffer is full and needs to be flushed
+    // 2) we have to use a kind of bounded threadsafe map to let the cache not grow indefinitely
+    private void writeCachedKey(final String name) {
+        /*  String k = cache.get(name);
+
+          if (k == null) {
+
+                  justWrite(QUOTE_CHAR);
+                  int start = bufferPos;
+                  writeEscaped0(name);
+                  int end = bufferPos;
+                  String escaped= get from buffer
+                  ---
+                  //FIXME if buffer is flushed this will not work here
+                  cache.putIfAbsent(name, escaped);
+                  justWrite(QUOTE_CHAR);
+                  justWrite(KEY_SEPARATOR);
+          }else*/
+        {
+            justWrite(QUOTE_CHAR);
+            writeEscaped0(name);
+            justWrite(QUOTE_CHAR);
+            justWrite(KEY_SEPARATOR);
+        }
+
+    }
+
+    @Override
+    public JsonGenerator writeStartObject() {
+        prepareValue();
+        state.push(GeneratorState.START_OBJECT);
+
+        writeIndent();
+        depth++;
+        justWrite(START_OBJECT_CHAR);
+
+        writeEol();
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartObject(final String name) {
+        checkObject(false);
+        writeKey(name);
+        justWrite(START_OBJECT_CHAR);
+        writeEol();
+        state.push(GeneratorState.START_OBJECT);
+        depth++;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartArray() {
+        prepareValue();
+        writeIndent();
+        state.push(GeneratorState.START_ARRAY);
+        justWrite(START_ARRAY_CHAR);
+        depth++;
+        writeEol();
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartArray(final String name) {
+        checkObject(false);
+        writeKey(name);
+        justWrite(START_ARRAY_CHAR);
+        writeEol();
+        state.push(GeneratorState.START_ARRAY);
+        depth++;
+        return this;
+    }
+
+    private void writeJsonValue(final String name, final JsonValue value) {
+        checkObject(false);
+        //TODO check null handling
+        switch (value.getValueType()) {
+            case ARRAY:
+                writeStartArray(name);
+                final JsonArray array = JsonArray.class.cast(value);
+                final Iterator<JsonValue> ait = array.iterator();
+                while (ait.hasNext()) {
+                    write(ait.next());
+                }
+                writeEnd();
+
+                break;
+            case OBJECT:
+                writeStartObject(name);
+                final JsonObject object = JsonObject.class.cast(value);
+                final Iterator<Map.Entry<String, JsonValue>> oit = object.entrySet().iterator();
+                while (oit.hasNext()) {
+                    final Map.Entry<String, JsonValue> keyval = oit.next();
+                    write(keyval.getKey(), keyval.getValue());
+                }
+                writeEnd();
+
+                break;
+            case STRING:
+                write(name, JsonString.class.cast(value).getString());
+                break;
+            case NUMBER:
+                //TODO optimize
+                final JsonNumber number = JsonNumber.class.cast(value);
+                if (number.isIntegral()) {
+                    write(name, number.longValueExact());
+                } else {
+                    write(name, number.bigDecimalValue());
+                }
+                break;
+            case TRUE:
+                write(name, true);
+                break;
+            case FALSE:
+                write(name, false);
+                break;
+            case NULL:
+                writeNull(name);
+                break;
+            default:
+                throw new JsonGenerationException("Unknown JsonValue type");
+        }
+    }
+
+    private void writeJsonValue(final JsonValue value) {
+        checkArray(true);
+        //TODO check null handling
+        switch (value.getValueType()) {
+            case ARRAY:
+                writeStartArray();
+                final JsonArray array = JsonArray.class.cast(value);
+                final Iterator<JsonValue> ait = array.iterator();
+                while (ait.hasNext()) {
+                    write(ait.next());
+                }
+                writeEnd();
+
+                break;
+            case OBJECT:
+                writeStartObject();
+                final JsonObject object = JsonObject.class.cast(value);
+                final Iterator<Map.Entry<String, JsonValue>> oit = object.entrySet().iterator();
+                while (oit.hasNext()) {
+                    final Map.Entry<String, JsonValue> keyval = oit.next();
+                    write(keyval.getKey(), keyval.getValue());
+                }
+                writeEnd();
+
+                break;
+            case STRING:
+                write(JsonString.class.cast(value).getString());
+                break;
+            case NUMBER:
+                //TODO optimize
+                final JsonNumber number = JsonNumber.class.cast(value);
+                if (number.isIntegral()) {
+                    write(number.longValueExact());
+                } else {
+                    write(number.bigDecimalValue());
+                }
+                break;
+            case TRUE:
+                write(true);
+                break;
+            case FALSE:
+                write(false);
+                break;
+            case NULL:
+                writeNull();
+                break;
+            default:
+                throw new JsonGenerationException("Unknown JsonValue type");
+        }
+    }
+
+    @Override
+    public JsonGenerator write(final String name, final JsonValue value) {
+        checkObject(false);
+        writeJsonValue(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final String name, final String value) {
+        checkObject(false);
+        writeKey(name);
+        writeValueAsJsonString(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final String name, final BigInteger value) {
+        checkObject(false);
+        writeKey(name);
+        writeValue(String.valueOf(value));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final String name, final BigDecimal value) {
+        checkObject(false);
+        writeKey(name);
+        writeValue(String.valueOf(value));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final String name, final int value) {
+        checkObject(false);
+        writeKey(name);
+        writeValue(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final String name, final long value) {
+        checkObject(false);
+        writeKey(name);
+        writeValue(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final String name, final double value) {
+        checkObject(false);
+        checkDoubleRange(value);
+        writeKey(name);
+        writeValue(String.valueOf(value));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final String name, final boolean value) {
+        checkObject(false);
+        writeKey(name);
+        writeValue(String.valueOf(value));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeNull(final String name) {
+        checkObject(false);
+        writeKey(name);
+        writeValue(NULL);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeEnd() {
+        checkArrayOrObject(false);
+        final GeneratorState last = state.pop();
+        depth--;
+        if (last != GeneratorState.START_ARRAY) {
+            writeEol();
+        }
+        writeIndent();
+        if (last == GeneratorState.IN_ARRAY || last == GeneratorState.START_ARRAY) {
+            justWrite(END_ARRAY_CHAR);
+        } else {
+            justWrite(END_OBJECT_CHAR);
+        }
+        alignState();
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final JsonValue value) {
+        checkArray(true);
+        writeJsonValue(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final String value) {
+        checkArray(false);
+        writeValueAsJsonString(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final BigDecimal value) {
+        checkArray(false);
+        writeValue(String.valueOf(value));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final BigInteger value) {
+        checkArray(false);
+        writeValue(String.valueOf(value));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final int value) {
+        checkArray(false);
+        writeValue(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final long value) {
+        checkArray(false);
+        writeValue(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final double value) {
+        checkArray(false);
+        checkDoubleRange(value);
+        writeValue(String.valueOf(value));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(final boolean value) {
+        checkArray(false);
+        writeValue(String.valueOf(value));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeNull() {
+        checkArray(false);
+        writeValue(NULL);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeKey(final String key) {
+        final GeneratorState currentState = currentState();
+        if (!currentState.acceptsKey) {
+            throw new JsonGenerationException("state " + currentState + " does not accept a key");
+        }
+        if (currentState == GeneratorState.IN_OBJECT) {
+            justWrite(COMMA_CHAR);
+            writeEol();
+        }
+
+        writeIndent();
+
+        writeCachedKey(key);
+        state.push(GeneratorState.AFTER_KEY);
+        return this;
+    }
+
+
+
+    @Override
+    public void close() {
+        try {
+            if (currentState() != GeneratorState.END) {
+                throw new JsonGenerationException("Invalid json");
+            }
+        } finally {
+            flushBuffer();
+            try {
+                writer.close();
+            } catch (final IOException e) {
+                throw new JsonException(e.getMessage(), e);
+            } finally {
+                bufferProvider.release(buffer);
+            }
+        }
+    }
+
+    @Override
+    public void flush() {
+        flushBuffer();
+        try {
+            writer.flush();
+        } catch (final IOException e) {
+            throw new JsonException(e.getMessage(), e);
+        }
+    }
+
+    private void flushBuffer() {
+        if (bufferPos > 0) {
+            try {
+                writer.write(buffer, 0, bufferPos);
+                bufferPos = 0;
+            } catch (final IOException e) {
+                throw new JsonException(e.getMessage(), e);
+            }
+        }
+    }
+
+    private void writeEscaped0(final String value) {
+        int len = 0;
+        if (value == null || (len = value.length()) == 0) {
+            return;
+        }
+
+        for (int i = 0; i < len; i++) {
+            char c = value.charAt(i);
+
+            while (c != ESCAPE_CHAR && c != QUOTE_CHAR && c >= SPACE) {
+
+                //read fast
+                justWrite(c);
+
+                if (i >= len - 1) {
+                    return;
+                }
+
+                i++;
+                c = value.charAt(i);
+            }
+
+            switch (c) {
+                case QUOTE_CHAR:
+                case ESCAPE_CHAR:
+                    justWrite(ESCAPE_CHAR);
+                    justWrite(c);
+                    break;
+                default:
+                    if (c < SPACE) {
+                        switch (c) {
+                            case EOL:
+                                justWrite("\\n");
+                                break;
+                            case '\r':
+                                justWrite("\\r");
+                                break;
+                            case '\t':
+                                justWrite("\\t");
+                                break;
+                            case '\b':
+                                justWrite("\\b");
+                                break;
+                            case '\f':
+                                justWrite("\\f");
+                                break;
+                            default:
+                                justWrite(toUnicode(c));
+                        }
+                    } else if ((c >= '\u0080' && c < '\u00a0') || (c >= '\u2000' && c < '\u2100')) {
+                        justWrite(toUnicode(c));
+                    } else {
+                        justWrite(c);
+                    }
+            }
+        }
+    }
+
+    private static final String UNICODE_PREFIX = "\\u";
+    private static final String UNICODE_PREFIX_HELPER = "000";
+
+    private static String toUnicode(final char c) {
+        final String hex = UNICODE_PREFIX_HELPER + Integer.toHexString(c);
+        final String s = UNICODE_PREFIX + hex.substring(hex.length() - 4);
+        return s;
+    }
+
+    private void justWrite(final String value) {
+        final int valueLength = value.length();
+
+        if (bufferPos + valueLength >= buffer.length) {
+
+            int start = 0;
+            int len = buffer.length - bufferPos;
+
+            while (true) {
+                int end = start + len;
+                if (end > valueLength) {
+                    end = valueLength;
+                }
+
+                value.getChars(start, end, buffer, bufferPos);
+
+                bufferPos += (end - start);
+                start += (len);
+
+                if (start >= valueLength) {
+                    return;
+                }
+
+                if (bufferPos >= buffer.length) {
+                    flushBuffer();
+                    len = buffer.length;
+                }
+            }
+        } else {
+            //fits completely into the buffer
+            value.getChars(0, valueLength, buffer, bufferPos);
+            bufferPos += valueLength;
+        }
+    }
+
+    private void justWrite(final char value) {
+        if (bufferPos >= buffer.length) {
+            flushBuffer();
+        }
+        buffer[bufferPos++] = value;
+    }
+
+    private void checkObject(final boolean allowInitial) {
+        final GeneratorState currentState = currentState();
+        if (currentState != GeneratorState.IN_OBJECT && currentState != GeneratorState.START_OBJECT) {
+            if (!allowInitial || currentState != GeneratorState.INITIAL) {
+                throw new JsonGenerationException("write(name, param) is only valid in objects");
+            }
+        }
+    }
+
+    private void checkArray(final boolean allowInitial) {
+        final GeneratorState currentState = currentState();
+        if (currentState != GeneratorState.IN_ARRAY && currentState != GeneratorState.START_ARRAY) {
+            if (!allowInitial || currentState != GeneratorState.INITIAL) {
+                throw new JsonGenerationException("write(param) is only valid in arrays");
+            }
+        }
+    }
+
+    private void checkArrayOrObject(final boolean allowInitial) {
+        final GeneratorState currentState = currentState();
+        if (currentState != GeneratorState.IN_ARRAY && currentState != GeneratorState.START_ARRAY
+                && currentState != GeneratorState.IN_OBJECT && currentState != GeneratorState.START_OBJECT) {
+            if (!allowInitial || currentState != GeneratorState.INITIAL) {
+                throw new JsonGenerationException("only valid within array or object");
+            }
+        }
+    }
+
+    private static void checkDoubleRange(final double value) {
+        if (Double.isInfinite(value) || Double.isNaN(value)) {
+            throw new NumberFormatException("double can't be infinite or NaN");
+        }
+    }
+
+    private void prepareValue() {
+        final GeneratorState currentState = currentState();
+        if (!currentState.acceptsValue) {
+            throw new JsonGenerationException("state " + currentState + " does not accept a value");
+        }
+        if (currentState == GeneratorState.IN_ARRAY) {
+            justWrite(',');
+            writeEol();
+        }
+    }
+
+    private void alignState() {
+
+        if (currentState() == GeneratorState.AFTER_KEY) {
+            state.pop();
+        }
+        switch (currentState()) {
+            case START_ARRAY:
+                swapState(GeneratorState.IN_ARRAY);
+                break;
+            case START_OBJECT:
+                swapState(GeneratorState.IN_OBJECT);
+                break;
+            case INITIAL:
+                swapState(GeneratorState.END);
+                break;
+            default:
+        }
+    }
+
+    private void swapState(final GeneratorState newState) {
+        state.pop();
+        state.push(newState);
+    }
+
+    private GeneratorState currentState() {
+        return state.peek();
+    }
+
+    private void writeValueAsJsonString(final String value) {
+        prepareValue();
+        final GeneratorState peek = state.peek();
+        if (peek == GeneratorState.START_ARRAY || peek == GeneratorState.IN_ARRAY) {
+            writeIndent();
+        }
+        justWrite(QUOTE_CHAR);
+        writeEscaped0(value);
+        justWrite(QUOTE_CHAR);
+        alignState();
+    }
+
+    private void writeValue(final String value) {
+        prepareValue();
+        final GeneratorState peek = state.peek();
+        if (peek == GeneratorState.START_ARRAY || peek == GeneratorState.IN_ARRAY) {
+            writeIndent();
+        }
+        justWrite(String.valueOf(value));
+        alignState();
+    }
+
+    private void writeValue(final int value) {
+        prepareValue();
+        final GeneratorState peek = state.peek();
+        if (peek == GeneratorState.START_ARRAY || peek == GeneratorState.IN_ARRAY) {
+            writeIndent();
+        }
+        writeInt0(value);
+        alignState();
+    }
+
+    private void writeValue(final long value) {
+        prepareValue();
+        final GeneratorState peek = state.peek();
+        if (peek == GeneratorState.START_ARRAY || peek == GeneratorState.IN_ARRAY) {
+            writeIndent();
+        }
+        writeLong0(value);
+        alignState();
+    }
+
+    //unoptimized, see below
+    private void writeLong0(final long i) {
+        justWrite(String.valueOf(i));
+    }
+
+    //unoptimized, see below
+    private void writeInt0(final int i) {
+        justWrite(String.valueOf(i));
+    }
+
+    //optimized number optimizations
+    /*
+        private void writeLong0(final long i) {
+            if (i == Long.MIN_VALUE) {
+                justWrite("-9223372036854775808");
+                return;
+            }
+            final int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
+            final char[] buf = new char[size];
+            getChars(i, size, buf);
+            justWrite(buf);
+        }
+
+        private void writeInt0(final int i) {
+            if (i == Integer.MIN_VALUE) {
+                justWrite("-2147483648");
+                return;
+            }
+            final int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
+            final char[] buf = new char[size];
+            getChars(i, size, buf);
+            justWrite(buf);
+        }
+
+        private final static char[] DIGIT_TENS = { '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '1', '1', '1', '1', '1', '1', '1', '1',
+                '1', '1', '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', '3', '3', '3', '3', '3', '3', '3', '3', '3', '3', '4', '4', '4',
+                '4', '4', '4', '4', '4', '4', '4', '5', '5', '5', '5', '5', '5', '5', '5', '5', '5', '6', '6', '6', '6', '6', '6', '6', '6',
+                '6', '6', '7', '7', '7', '7', '7', '7', '7', '7', '7', '7', '8', '8', '8', '8', '8', '8', '8', '8', '8', '8', '9', '9', '9',
+                '9', '9', '9', '9', '9', '9', '9', };
+
+        private final static char[] DIGIT_ONES = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7',
+                '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2',
+                '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7',
+                '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2',
+                '3', '4', '5', '6', '7', '8', '9', };
+
+        private final static char[] DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
+                'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
+
+        // Requires positive x
+        private static int stringSize(final long x) {
+            long p = 10;
+            for (int i = 1; i < 19; i++) {
+                if (x < p) {
+                    return i;
+                }
+                p = 10 * p;
+            }
+            return 19;
+        }
+
+        private static void getChars(long i, final int index, final char[] buf) {
+            long q;
+            int r;
+            int charPos = index;
+            char sign = 0;
+
+            if (i < 0) {
+                sign = '-';
+                i = -i;
+            }
+
+            // Get 2 digits/iteration using longs until quotient fits into an int
+            while (i > Integer.MAX_VALUE) {
+                q = i / 100;
+                // really: r = i - (q * 100);
+                r = (int) (i - ((q << 6) + (q << 5) + (q << 2)));
+                i = q;
+                buf[--charPos] = DIGIT_ONES[r];
+                buf[--charPos] = DIGIT_TENS[r];
+            }
+
+            // Get 2 digits/iteration using ints
+            int q2;
+            int i2 = (int) i;
+            while (i2 >= 65536) {
+                q2 = i2 / 100;
+                // really: r = i2 - (q * 100);
+                r = i2 - ((q2 << 6) + (q2 << 5) + (q2 << 2));
+                i2 = q2;
+                buf[--charPos] = DIGIT_ONES[r];
+                buf[--charPos] = DIGIT_TENS[r];
+            }
+
+            // Fall thru to fast mode for smaller numbers
+            // assert(i2 <= 65536, i2);
+            for (;;) {
+                q2 = (i2 * 52429) >>> (16 + 3);
+                r = i2 - ((q2 << 3) + (q2 << 1)); // r = i2-(q2*10) ...
+                buf[--charPos] = DIGITS[r];
+                i2 = q2;
+                if (i2 == 0) {
+                    break;
+                }
+            }
+            if (sign != 0) {
+                buf[--charPos] = sign;
+            }
+        }
+     */
+
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonInMemoryParser.java b/src/main/java/org/apache/johnzon/core/JsonInMemoryParser.java
new file mode 100644
index 0000000..1f2e33e
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonInMemoryParser.java
@@ -0,0 +1,314 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import java.math.BigDecimal;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.stream.Stream;
+
+import javax.json.JsonArray;
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import javax.json.JsonString;
+import javax.json.JsonValue;
+import javax.json.JsonValue.ValueType;
+import javax.json.stream.JsonLocation;
+
+class JsonInMemoryParser implements JohnzonJsonParser {
+
+    private final SimpleStack<Iterator<Event>> stack = new SimpleStack<Iterator<Event>>();
+
+    private Event currentEvent;
+    private JsonValue currentValue;
+
+    private class ArrayIterator implements Iterator<Event> {
+
+        private final Iterator<JsonValue> aentries;
+        private Boolean end = null;
+
+        public ArrayIterator(final JsonArray ja) {
+            aentries = ja.iterator();
+
+        }
+
+        @Override
+        public boolean hasNext() {
+            return !Boolean.TRUE.equals(end);
+        }
+
+        @Override
+        public Event next() {
+
+            if (end == null) {
+                end = Boolean.FALSE;
+                return Event.START_ARRAY;
+            } else if (!aentries.hasNext()) {
+
+                if (!stack.isEmpty()) {
+                    stack.pop();
+                }
+
+                end = Boolean.TRUE;
+
+                return Event.END_ARRAY;
+            } else {
+
+                final JsonValue val = aentries.next();
+
+                final ValueType vt = val.getValueType();
+
+                if (vt == ValueType.OBJECT) {
+                    stack.push(new ObjectIterator((JsonObject) val));
+                    return stack.peek().next();
+
+                } else if (vt == ValueType.ARRAY) {
+                    stack.push(new ArrayIterator((JsonArray) val));
+                    return stack.peek().next();
+
+                } else {
+                    currentValue = val;
+                    return getEvent(vt);
+                }
+            }
+
+        }
+
+        @Override
+        public void remove() {
+            throw new UnsupportedOperationException();
+
+        }
+
+    }
+
+    private class ObjectIterator implements Iterator<Event> {
+
+        private final Iterator<Map.Entry<String, JsonValue>> oentries;
+        private JsonValue jsonValue;
+        private Boolean end = null;
+
+        public ObjectIterator(final JsonObject jo) {
+            oentries = jo.entrySet().iterator();
+
+        }
+
+        @Override
+        public boolean hasNext() {
+            return !Boolean.TRUE.equals(end);
+        }
+
+        @Override
+        public Event next() {
+
+            if (end == null) {
+                end = Boolean.FALSE;
+                return Event.START_OBJECT;
+            } else if (jsonValue == null && !oentries.hasNext()) {
+
+                if (!stack.isEmpty()) {
+                    stack.pop();
+                }
+
+                end = Boolean.TRUE;
+
+                return Event.END_OBJECT;
+            } else if (jsonValue == null) {
+
+                final Map.Entry<String, JsonValue> tmp = oentries.next();
+                jsonValue = tmp.getValue();
+                currentValue = new JsonStringImpl(tmp.getKey());
+                return Event.KEY_NAME;
+
+            } else {
+
+                final ValueType vt = jsonValue.getValueType();
+
+                if (vt == ValueType.OBJECT) {
+                    stack.push(new ObjectIterator((JsonObject) jsonValue));
+                    jsonValue = null;
+                    return stack.peek().next();
+
+                } else if (vt == ValueType.ARRAY) {
+                    stack.push(new ArrayIterator((JsonArray) jsonValue));
+                    jsonValue = null;
+                    return stack.peek().next();
+
+                } else {
+
+                    final Event ret = getEvent(vt);
+                    currentValue = jsonValue;
+                    jsonValue = null;
+                    return ret;
+                }
+
+            }
+
+        }
+
+        @Override
+        public void remove() {
+            throw new UnsupportedOperationException();
+
+        }
+
+    }
+
+    private static Event getEvent(final ValueType value) {
+
+        switch (value) {
+        case NUMBER:
+            return Event.VALUE_NUMBER;
+        case STRING:
+            return Event.VALUE_STRING;
+        case FALSE:
+            return Event.VALUE_FALSE;
+        case NULL:
+            return Event.VALUE_NULL;
+        case TRUE:
+            return Event.VALUE_TRUE;
+        default:
+            throw new IllegalArgumentException(value + " not supported");
+
+        }
+
+    }
+
+    JsonInMemoryParser(final JsonObject object) {
+        stack.push(new ObjectIterator(object));
+    }
+
+    JsonInMemoryParser(final JsonArray array) {
+        stack.push(new ArrayIterator(array));
+    }
+
+    @Override
+    public boolean hasNext() {
+        return !stack.isEmpty();
+    }
+
+    @Override
+    public Event next() {
+
+        if (!hasNext()) {
+            throw new NoSuchElementException();
+        }
+
+        currentEvent = stack.peek().next();
+
+        return currentEvent;
+    }
+
+    @Override
+    public String getString() {
+        if (currentEvent != Event.KEY_NAME && currentEvent != Event.VALUE_STRING) {
+            throw new IllegalStateException("String is for numbers and strings");
+        }
+        return JsonString.class.cast(currentValue).getString();
+    }
+
+    @Override
+    public boolean isIntegralNumber() {
+        if (currentEvent != Event.VALUE_NUMBER) {
+            throw new IllegalStateException("isIntegralNumber is for numbers");
+        }
+        return JsonNumber.class.cast(currentValue).isIntegral();
+    }
+
+    @Override
+    public boolean isNotTooLong() {
+        return true;
+    }
+
+    @Override
+    public int getInt() {
+        if (currentEvent != Event.VALUE_NUMBER) {
+            throw new IllegalStateException("getInt is for numbers");
+        }
+        return JsonNumber.class.cast(currentValue).intValue();
+    }
+
+    @Override
+    public long getLong() {
+        if (currentEvent != Event.VALUE_NUMBER) {
+            throw new IllegalStateException("getLong is for numbers");
+        }
+        return JsonNumber.class.cast(currentValue).longValue();
+    }
+
+    @Override
+    public BigDecimal getBigDecimal() {
+        if (currentEvent != Event.VALUE_NUMBER) {
+            throw new IllegalStateException("getBigDecimal is for numbers");
+        }
+        return JsonNumber.class.cast(currentValue).bigDecimalValue();
+    }
+
+    @Override
+    public JsonLocation getLocation() { // no location for in memory parsers
+        return JsonLocationImpl.UNKNOWN_LOCATION;
+    }
+
+    @Override
+    public void close() {
+        // no-op
+    }
+
+    @Override
+    public JsonObject getObject() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public JsonValue getValue() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public JsonArray getArray() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Stream<JsonValue> getArrayStream() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Stream<Map.Entry<String, JsonValue>> getObjectStream() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Stream<JsonValue> getValueStream() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void skipArray() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void skipObject() {
+        throw new UnsupportedOperationException();
+    }
+
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonLocationImpl.java b/src/main/java/org/apache/johnzon/core/JsonLocationImpl.java
new file mode 100644
index 0000000..11fd074
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonLocationImpl.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import java.io.Serializable;
+
+import javax.json.stream.JsonLocation;
+
+final class JsonLocationImpl implements JsonLocation, Serializable {
+    
+    public static final JsonLocation UNKNOWN_LOCATION = new JsonLocationImpl(-1, -1, -1);
+    
+    private final long lineNumber;
+    private final long columnNumber;
+    private final long streamOffset;
+
+    JsonLocationImpl(final long lineNumber, final long columnNumber, final long streamOffset) {
+        this.lineNumber = lineNumber;
+        this.columnNumber = columnNumber;
+        this.streamOffset = streamOffset;
+    }
+
+    @Override
+    public long getLineNumber() {
+        return lineNumber;
+    }
+
+    @Override
+    public long getColumnNumber() {
+        return columnNumber;
+    }
+
+    @Override
+    public long getStreamOffset() {
+        return streamOffset;
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final JsonLocationImpl that = JsonLocationImpl.class.cast(o);
+        return columnNumber == that.columnNumber && lineNumber == that.lineNumber && streamOffset == that.streamOffset;
+
+    }
+
+    @Override
+    public int hashCode() {
+        int result = (int) (lineNumber ^ (lineNumber >>> 32));
+        result = 31 * result + (int) (columnNumber ^ (columnNumber >>> 32));
+        result = 31 * result + (int) (streamOffset ^ (streamOffset >>> 32));
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "[lineNumber=" + lineNumber + ", columnNumber=" + columnNumber + ", streamOffset=" + streamOffset + "]";
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonLongImpl.java b/src/main/java/org/apache/johnzon/core/JsonLongImpl.java
new file mode 100644
index 0000000..c9ae38f
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonLongImpl.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import javax.json.JsonArray;
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+public final class JsonLongImpl implements JsonNumber, Serializable {
+    private final long value;
+    private Integer hashCode = null;
+
+    JsonLongImpl(final long value) {
+        this.value = value;
+    }
+
+    @Override
+    public Number numberValue() {
+        return value;
+    }
+
+    @Override
+    public boolean isIntegral() {
+        return true;
+    }
+
+    @Override
+    public int intValue() {
+        return (int) value;
+    }
+
+    @Override
+    public int intValueExact() {
+        return intValue();
+    }
+
+    @Override
+    public long longValue() {
+        return value;
+    }
+
+    @Override
+    public long longValueExact() {
+        return value;
+    }
+
+    @Override
+    public BigInteger bigIntegerValue() {
+        return new BigInteger(toString());
+    }
+
+    @Override
+    public BigInteger bigIntegerValueExact() {
+        return bigIntegerValue();
+    }
+
+    @Override
+    public double doubleValue() {
+        return value;
+    }
+
+    @Override
+    public BigDecimal bigDecimalValue() {
+        return new BigDecimal(toString());
+    }
+
+    @Override
+    public ValueType getValueType() {
+        return ValueType.NUMBER;
+    }
+
+    @Override
+    public String toString() {
+        return Long.toString(value);
+    }
+
+    @Override
+    public int hashCode() {
+        if (hashCode == null) {
+            hashCode = bigDecimalValue().hashCode();
+        }
+        return hashCode;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (JsonLongImpl.class.isInstance(obj)) {
+            return JsonLongImpl.class.cast(obj).value == value;
+        }
+        return JsonNumber.class.isInstance(obj) && JsonNumber.class.cast(obj).bigDecimalValue().equals(bigDecimalValue());
+    }
+
+    @Override
+    public JsonObject asJsonObject() {
+        return JsonObject.class.cast(this);
+    }
+
+    @Override
+    public JsonArray asJsonArray() {
+        return JsonArray.class.cast(this);
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonMergePatchDiff.java b/src/main/java/org/apache/johnzon/core/JsonMergePatchDiff.java
new file mode 100644
index 0000000..5175e8d
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonMergePatchDiff.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.johnzon.core;
+
+import java.util.Map;
+
+import javax.json.JsonMergePatch;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonValue;
+
+/**
+ * Creates a JsonMergePatch as diff between two JsonValues
+ */
+class JsonMergePatchDiff extends DiffBase {
+    private final JsonValue source;
+    private final JsonValue target;
+
+    public JsonMergePatchDiff(JsonValue source, JsonValue target) {
+        this.source = source;
+        this.target = target;
+    }
+
+    public JsonMergePatch calculateDiff() {
+        return new JsonMergePatchImpl(diff(source, target));
+    }
+
+    private JsonValue diff(JsonValue source, JsonValue target) {
+        JsonObjectBuilder builder = new JsonObjectBuilderImpl();
+
+        if (isJsonObject(source) && isJsonObject(target)) {
+            JsonObject srcObj = source.asJsonObject();
+            JsonObject targetObj = target.asJsonObject();
+            for (Map.Entry<String, JsonValue> sourceEntry : srcObj.entrySet()) {
+                String attributeName = sourceEntry.getKey();
+                if (targetObj.containsKey(attributeName)) {
+                    // compare the attribute values
+                    JsonValue attribDiff = diff(sourceEntry.getValue(), targetObj.get(attributeName));
+                    if (!JsonValue.EMPTY_JSON_OBJECT.equals(attribDiff)) {
+                        builder.add(attributeName, attribDiff);
+                    }
+                } else {
+                    // attribute got removed
+                    builder.add(attributeName, JsonValue.NULL);
+                }
+            }
+
+            for (Map.Entry<String, JsonValue> targetEntry : targetObj.entrySet()) {
+                String attributeName = targetEntry.getKey();
+                if (!srcObj.containsKey(attributeName)) {
+                    // add operation
+                    builder.add(attributeName, targetEntry.getValue());
+                }
+            }
+
+            return builder.build();
+        } else if (source.equals(target)) {
+            // if the two objects are identical, then return an empty patch
+            return JsonValue.EMPTY_JSON_OBJECT;
+        } else {
+            // as defined in the RFC anything else than comparing JsonObjects will result
+            // in completely replacing the source with the target
+            // That means our target is the patch.
+            return target;
+        }
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonMergePatchImpl.java b/src/main/java/org/apache/johnzon/core/JsonMergePatchImpl.java
new file mode 100644
index 0000000..b3f55c6
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonMergePatchImpl.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.johnzon.core;
+
+
+import java.util.Map;
+
+import javax.json.JsonMergePatch;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonValue;
+
+/**
+ * @author <a href="mailto:struberg@yahoo.de">Mark Struberg</a>
+ */
+public class JsonMergePatchImpl implements JsonMergePatch {
+    private final JsonValue patch;
+
+    public JsonMergePatchImpl(JsonValue patch) {
+        this.patch = patch;
+    }
+
+    @Override
+    public JsonValue apply(JsonValue valueToApplyPatchOn) {
+        return applyPatch(valueToApplyPatchOn, patch);
+    }
+
+    private JsonValue applyPatch(JsonValue valueToApplyPatchOn, JsonValue patch) {
+        if (patch == null) {
+            return JsonValue.NULL;
+        } else if (patch instanceof JsonObject && valueToApplyPatchOn instanceof JsonObject) {
+            // we only apply an actual patch IF both sides are a JsonObject
+            JsonObject patchObject = patch.asJsonObject();
+
+            return applyJsonObjectPatch(valueToApplyPatchOn.asJsonObject(), patchObject);
+        } else {
+            // this must be a native JsonValue or JsonObject, so we just replace the
+            // the whole original valueToApplyPatchOn with the new jsonValue
+            return patch;
+        }
+    }
+
+    private JsonValue applyJsonObjectPatch(JsonObject jsonObject, JsonObject patch) {
+        JsonObjectBuilder builder = new JsonObjectBuilderImpl(jsonObject);
+
+        for (Map.Entry<String, JsonValue> patchAttrib : patch.entrySet()) {
+            String attribName = patchAttrib.getKey();
+            if (patchAttrib.getValue().equals(JsonValue.NULL)) {
+                builder.remove(attribName);
+            } else {
+                JsonValue originalAttrib = jsonObject.get(attribName);
+                if (originalAttrib == null) {
+                    builder.add(attribName, patchAttrib.getValue());
+                } else {
+                    builder.add(attribName, applyPatch(originalAttrib, patchAttrib.getValue()));
+                }
+            }
+        }
+        return builder.build();
+    }
+
+    @Override
+    public JsonValue toJsonValue() {
+        return patch;
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonNumberImpl.java b/src/main/java/org/apache/johnzon/core/JsonNumberImpl.java
new file mode 100644
index 0000000..7ffe348
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonNumberImpl.java
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import javax.json.JsonArray;
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+final class JsonNumberImpl implements JsonNumber, Serializable {
+    private final BigDecimal value;
+    private transient Integer hashCode = null;
+
+    JsonNumberImpl(final BigDecimal decimal) {
+        if (decimal == null) {
+            throw new NullPointerException("decimal must not be null");
+        }
+
+        this.value = decimal;
+    }
+
+    @Override
+    public Number numberValue() {
+        return value;
+    }
+
+    @Override
+    public boolean isIntegral() {
+        return value.scale() == 0;
+    }
+
+    @Override
+    public int intValue() {
+        return value.intValue();
+    }
+
+    @Override
+    public int intValueExact() {
+        checkFractionalPart();
+        return value.intValueExact();
+    }
+
+    @Override
+    public long longValue() {
+        return value.longValue();
+    }
+
+    @Override
+    public long longValueExact() {
+        checkFractionalPart();
+        return value.longValueExact();
+    }
+
+    @Override
+    public BigInteger bigIntegerValue() {
+        return value.toBigInteger();
+    }
+
+    @Override
+    public BigInteger bigIntegerValueExact() {
+        return value.toBigIntegerExact();
+    }
+
+    @Override
+    public double doubleValue() {
+        return value.doubleValue();
+    }
+
+    @Override
+    public BigDecimal bigDecimalValue() {
+        return value;
+    }
+
+    @Override
+    public ValueType getValueType() {
+        return ValueType.NUMBER;
+    }
+
+    @Override
+    public String toString() {
+        return value.toString();
+    }
+
+    @Override
+    public int hashCode() {
+        Integer h = hashCode;
+        if (h == null) {
+            h = value.hashCode();
+            hashCode = h;
+        }
+        return h;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return JsonNumber.class.isInstance(obj) && JsonNumber.class.cast(obj).bigDecimalValue().equals(value);
+    }
+
+    private void checkFractionalPart() {
+        if (value.remainder(BigDecimal.ONE).doubleValue() != 0) {
+            throw new ArithmeticException("Not an int/long, use other value readers");
+        }
+    }
+
+    @Override
+    public JsonObject asJsonObject() {
+        return JsonObject.class.cast(this);
+    }
+
+    @Override
+    public JsonArray asJsonArray() {
+        return JsonArray.class.cast(this);
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonObjectBuilderImpl.java b/src/main/java/org/apache/johnzon/core/JsonObjectBuilderImpl.java
new file mode 100644
index 0000000..69a2adb
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonObjectBuilderImpl.java
@@ -0,0 +1,178 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonException;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonValue;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+class JsonObjectBuilderImpl implements JsonObjectBuilder, Serializable {
+    private Map<String, JsonValue> attributeMap = new LinkedHashMap<>();
+
+    public JsonObjectBuilderImpl() {
+    }
+
+    public JsonObjectBuilderImpl(JsonObject initialData) {
+        attributeMap = new LinkedHashMap<>(initialData);
+    }
+
+    public JsonObjectBuilderImpl(Map<String, Object> initialValues) {
+        this();
+        for (Map.Entry<String, Object> entry : initialValues.entrySet()) {
+            add(entry.getKey(), entry.getValue());
+        }
+    }
+
+
+    /**
+     * Internal method to add a value where we do not yet know the type at compile time.
+     */
+    public void add(final String name, final Object value) {
+        if (value instanceof JsonValue) {
+            add(name, (JsonValue) value);
+        } else if (value instanceof BigDecimal) {
+            add(name, (BigDecimal) value);
+        } else if (value instanceof BigInteger) {
+            add(name, (BigInteger) value);
+        } else if (value instanceof Boolean) {
+            add(name, (boolean) value);
+        } else if (value instanceof Double) {
+            add(name, (double) value);
+        } else if (value instanceof Integer) {
+            add(name, (int) value);
+        } else if (value instanceof Long) {
+            add(name, (long) value);
+        } else if (value instanceof String) {
+            add(name, (String) value);
+        } else if (value == null) {
+            addNull(name);
+        } else {
+            throw new JsonException("Illegal JSON type! name=" + name + " type=" + value.getClass());
+        }
+    }
+
+    @Override
+    public JsonObjectBuilder add(final String name, final JsonValue value) {
+        putValue(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder add(final String name, final String value) {
+        putValue(name, new JsonStringImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder add(final String name, final BigInteger value) {
+        putValue(name, new JsonNumberImpl(new BigDecimal(value)));
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder add(final String name, final BigDecimal value) {
+        putValue(name, new JsonNumberImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder add(final String name, final int value) {
+        putValue(name, new JsonLongImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder add(final String name, final long value) {
+        putValue(name, new JsonLongImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder add(final String name, final double value) {
+        putValue(name, new JsonDoubleImpl(value));
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder add(final String name, final boolean value) {
+        putValue(name, value ? JsonValue.TRUE : JsonValue.FALSE);
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder addNull(final String name) {
+        putValue(name, JsonValue.NULL);
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder add(final String name, final JsonObjectBuilder builder) {
+        putValue(name, builder.build());
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder add(final String name, final JsonArrayBuilder builder) {
+        putValue(name, builder.build());
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder addAll(JsonObjectBuilder builder) {
+        attributeMap.putAll(builder.build());
+        return this;
+    }
+
+    @Override
+    public JsonObjectBuilder remove(String name) {
+        attributeMap.remove(name);
+        return this;
+    }
+
+    private void putValue(String name, JsonValue value){
+        if(name == null || value == null) {
+            throw new NullPointerException("name or value/builder must not be null");
+        }
+        
+        attributeMap.put(name, value);
+    }
+    
+
+    @Override
+    public JsonObject build() {
+        
+        if(attributeMap == null || attributeMap.isEmpty()) {
+            return new JsonObjectImpl(Collections.EMPTY_MAP);
+        } else {
+            Map<String, JsonValue> dump = (Collections.unmodifiableMap(attributeMap));
+            attributeMap =null;
+            return new JsonObjectImpl(dump);
+        }
+        
+        
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonObjectImpl.java b/src/main/java/org/apache/johnzon/core/JsonObjectImpl.java
new file mode 100644
index 0000000..8a81545
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonObjectImpl.java
@@ -0,0 +1,206 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import javax.json.JsonArray;
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import javax.json.JsonString;
+import javax.json.JsonValue;
+import javax.json.spi.JsonProvider;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.util.AbstractMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+
+final class JsonObjectImpl extends AbstractMap<String, JsonValue> implements JsonObject, Serializable {
+    private transient Integer hashCode = null;
+    private final Map<String, JsonValue> unmodifieableBackingMap;
+
+    private <T> T value(final String name, final Class<T> clazz) {
+        final JsonValue v = unmodifieableBackingMap.get(name);
+        if (v != null) {
+            return clazz.cast(v);
+        }
+        return null;
+    }
+
+    private <T> T valueOrExcpetion(final String name, final Class<T> clazz) {
+        T value = value(name, clazz);
+        if (value == null) {
+            throw new NullPointerException("no mapping for " + name);
+        }
+
+        return value;
+    }
+
+    JsonObjectImpl(final Map<String, JsonValue> backingMap) {
+        super();
+        this.unmodifieableBackingMap = backingMap;
+    }
+
+    @Override
+    public JsonArray getJsonArray(final String name) {
+        return value(name, JsonArray.class);
+    }
+
+    @Override
+    public JsonObject getJsonObject(final String name) {
+        return value(name, JsonObject.class);
+    }
+
+    @Override
+    public JsonNumber getJsonNumber(final String name) {
+        return value(name, JsonNumber.class);
+    }
+
+    @Override
+    public JsonString getJsonString(final String name) {
+        return value(name, JsonString.class);
+    }
+
+    @Override
+    public String getString(final String name) {
+        return valueOrExcpetion(name, JsonString.class).getString();
+    }
+
+    @Override
+    public String getString(final String name, final String defaultValue) {
+        final Object v = value(name, JsonValue.class);
+        if (v != null && v instanceof JsonString) {
+            return JsonString.class.cast(v).getString();
+        }
+
+        return defaultValue;
+    }
+
+    @Override
+    public int getInt(final String name) {
+        return valueOrExcpetion(name, JsonNumber.class).intValue();
+    }
+
+    @Override
+    public int getInt(final String name, final int defaultValue) {
+        final Object v = value(name, JsonValue.class);
+        if (v != null && v instanceof JsonNumber) {
+            return JsonNumber.class.cast(v).intValue();
+        }
+
+        return defaultValue;
+    }
+
+    @Override
+    public boolean getBoolean(final String name) {
+        final JsonValue obj = valueOrExcpetion(name, JsonValue.class);
+        if (JsonValue.TRUE == obj) {
+            return true;
+        }
+        if (JsonValue.FALSE == obj) {
+            return false;
+        }
+        throw new ClassCastException("Wrong value for a boolean: " + obj);
+    }
+
+    @Override
+    public boolean getBoolean(final String name, final boolean defaultValue) {
+        final Object v = value(name, JsonValue.class);
+        if (v != null) {
+            return JsonValue.TRUE.equals(v) || !JsonValue.FALSE.equals(v) && defaultValue;
+        }
+        return defaultValue;
+    }
+
+    @Override
+    public boolean isNull(final String name) {
+        return JsonValue.NULL.equals(valueOrExcpetion(name, JsonValue.class));
+    }
+
+    @Override
+    public ValueType getValueType() {
+        return ValueType.OBJECT;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder builder = new StringBuilder("{");
+        final Iterator<Map.Entry<String, JsonValue>> it = unmodifieableBackingMap.entrySet().iterator();
+        boolean hasNext = it.hasNext();
+        while (hasNext) {
+            final Map.Entry<String, JsonValue> entry = it.next();
+
+            builder.append('"').append(entry.getKey()).append("\":");
+
+            final JsonValue value = entry.getValue();
+            if (JsonString.class.isInstance(value)) {
+                builder.append(value.toString());
+            } else {
+                builder.append(value != JsonValue.NULL ? value.toString() : JsonChars.NULL);
+            }
+
+            hasNext = it.hasNext();
+            if (hasNext) {
+                builder.append(",");
+            }
+        }
+        return builder.append('}').toString();
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return JsonObjectImpl.class.isInstance(obj)
+                && unmodifieableBackingMap.equals(JsonObjectImpl.class.cast(obj).unmodifieableBackingMap);
+    }
+
+    @Override
+    public int hashCode() {
+        Integer h = hashCode;
+        if (h == null) {
+            h = unmodifieableBackingMap.hashCode();
+            hashCode = h;
+        }
+        return h;
+    }
+
+    @Override
+    public Set<java.util.Map.Entry<String, JsonValue>> entrySet() {
+        return unmodifieableBackingMap.entrySet();
+    }
+
+    private Object writeReplace() throws ObjectStreamException {
+        return new SerializableValue(toString());
+    }
+
+    @Override
+    public JsonValue getValue(String jsonPointer) {
+        return JsonProvider.provider().createPointer(jsonPointer).getValue(this);
+    }
+
+    @Override
+    public JsonObject asJsonObject() {
+        return JsonObject.class.cast(this);
+    }
+
+    @Override
+    public JsonArray asJsonArray() {
+        return JsonArray.class.cast(this);
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonParserFactoryImpl.java b/src/main/java/org/apache/johnzon/core/JsonParserFactoryImpl.java
new file mode 100644
index 0000000..440b4cb
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonParserFactoryImpl.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import javax.json.stream.JsonParser;
+import javax.json.stream.JsonParserFactory;
+import java.io.InputStream;
+import java.io.Reader;
+import java.nio.charset.Charset;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+import static java.util.Arrays.asList;
+
+public class JsonParserFactoryImpl extends AbstractJsonFactory implements JsonParserFactory {
+    public static final String MAX_STRING_LENGTH = "org.apache.johnzon.max-string-length";
+    public static final int DEFAULT_MAX_STRING_LENGTH = Integer.getInteger(MAX_STRING_LENGTH, 256 * 1024); //256kB
+    
+    public static final String AUTO_ADJUST_STRING_BUFFER = "org.apache.johnzon.auto-adjust-buffer";
+    public static final String BUFFER_LENGTH = "org.apache.johnzon.default-char-buffer";
+    public static final int DEFAULT_BUFFER_LENGTH = Integer.getInteger(BUFFER_LENGTH, 64 * 1024); //64k
+    
+    public static final String SUPPORTS_COMMENTS = "org.apache.johnzon.supports-comments";
+    public static final boolean DEFAULT_SUPPORTS_COMMENT = Boolean.getBoolean(SUPPORTS_COMMENTS); //default is false;
+
+    static final Collection<String> SUPPORTED_CONFIG_KEYS = asList(
+        BUFFER_STRATEGY, MAX_STRING_LENGTH, BUFFER_LENGTH, SUPPORTS_COMMENTS, AUTO_ADJUST_STRING_BUFFER
+    );
+      
+    private final int maxSize;
+    private final BufferStrategy.BufferProvider<char[]> bufferProvider;
+    private final BufferStrategy.BufferProvider<char[]> valueBufferProvider;
+    private final boolean supportsComments;
+    private final boolean autoAdjustBuffers;
+
+    JsonParserFactoryImpl(final Map<String, ?> config) {
+        super(config, SUPPORTED_CONFIG_KEYS, null);
+
+        final int bufferSize = getInt(BUFFER_LENGTH, DEFAULT_BUFFER_LENGTH);
+        if (bufferSize <= 0) {
+            throw new IllegalArgumentException("buffer length must be greater than zero");
+        }
+
+        this.maxSize = getInt(MAX_STRING_LENGTH, DEFAULT_MAX_STRING_LENGTH);
+        this.bufferProvider = getBufferProvider().newCharProvider(bufferSize);
+        this.valueBufferProvider = getBufferProvider().newCharProvider(maxSize);
+        this.supportsComments = getBool(SUPPORTS_COMMENTS, DEFAULT_SUPPORTS_COMMENT);
+        this.autoAdjustBuffers = getBool(AUTO_ADJUST_STRING_BUFFER, true);
+    }
+
+    private JsonStreamParserImpl getDefaultJsonParserImpl(final InputStream in) {
+        if (supportsComments) {
+            return new CommentsJsonStreamParserImpl(in, maxSize, bufferProvider, valueBufferProvider, autoAdjustBuffers);
+        }
+        //UTF Auto detection RFC 4627
+        return new JsonStreamParserImpl(in, maxSize, bufferProvider, valueBufferProvider, autoAdjustBuffers);
+    }
+
+    private JsonStreamParserImpl getDefaultJsonParserImpl(final InputStream in, final Charset charset) {
+        if (supportsComments) {
+            return new CommentsJsonStreamParserImpl(in, charset, maxSize, bufferProvider, valueBufferProvider, autoAdjustBuffers);
+        }
+        //use provided charset
+        return new JsonStreamParserImpl(in, charset, maxSize, bufferProvider, valueBufferProvider, autoAdjustBuffers);
+    }
+
+    private JsonStreamParserImpl getDefaultJsonParserImpl(final Reader in) {
+        if (supportsComments) {
+            return new CommentsJsonStreamParserImpl(in, maxSize, bufferProvider, valueBufferProvider, autoAdjustBuffers);
+        }
+        //no charset necessary
+        return new JsonStreamParserImpl(in, maxSize, bufferProvider, valueBufferProvider, autoAdjustBuffers);
+    }
+
+    @Override
+    public JsonParser createParser(final Reader reader) {
+        return getDefaultJsonParserImpl(reader);
+    }
+
+    @Override
+    public JsonParser createParser(final InputStream in) {
+        return getDefaultJsonParserImpl(in);
+    }
+
+    @Override
+    public JsonParser createParser(final InputStream in, final Charset charset) {
+        return getDefaultJsonParserImpl(in, charset);
+    }
+
+    @Override
+    public JsonParser createParser(final JsonObject obj) {
+        // no need of a comment version since JsonObject has no comment event
+        return new JsonInMemoryParser(obj);
+    }
+
+    @Override
+    public JsonParser createParser(final JsonArray array) {
+        // no need of a comment version since JsonObject has no comment event
+        return new JsonInMemoryParser(array);
+    }
+
+    @Override
+    public Map<String, ?> getConfigInUse() {
+        return Collections.unmodifiableMap(internalConfig);
+    }
+
+    public JsonStreamParserImpl createInternalParser(final InputStream in) {
+        return getDefaultJsonParserImpl(in);
+    }
+    
+    public JsonStreamParserImpl createInternalParser(final InputStream in, final Charset charset) {
+        return getDefaultJsonParserImpl(in, charset);
+    }
+
+    public JsonStreamParserImpl createInternalParser(final Reader reader) {
+        return getDefaultJsonParserImpl(reader);
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonPatchBuilderImpl.java b/src/main/java/org/apache/johnzon/core/JsonPatchBuilderImpl.java
new file mode 100644
index 0000000..ada2db7
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonPatchBuilderImpl.java
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.johnzon.core;
+
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import javax.json.JsonPatch;
+import javax.json.JsonPatchBuilder;
+import javax.json.JsonValue;
+import java.util.ArrayList;
+import java.util.List;
+
+class JsonPatchBuilderImpl implements JsonPatchBuilder {
+
+    private final List<JsonPatchImpl.PatchValue> operations;
+
+
+    JsonPatchBuilderImpl() {
+        operations = new ArrayList<>();
+    }
+
+    JsonPatchBuilderImpl(JsonArray initialData) {
+        operations = new ArrayList<>(initialData.size());
+
+        for (JsonValue value : initialData) {
+
+            JsonObject operation = (JsonObject) value;
+
+            JsonPatch.Operation op = JsonPatch.Operation.fromOperationName(operation.getString("op"));
+            String path = operation.getString("path");
+            String from = operation.getString("from", null);
+            JsonValue jsonValue = operation.get("value");
+
+            operations.add(new JsonPatchImpl.PatchValue(op,
+                                                        path,
+                                                        from,
+                                                        jsonValue));
+        }
+    }
+
+
+    @Override
+    public JsonPatchBuilder add(String path, JsonValue value) {
+        return addOperation(new JsonPatchImpl.PatchValue(JsonPatch.Operation.ADD,
+                                                         path,
+                                                         null,
+                                                         value));
+    }
+
+    @Override
+    public JsonPatchBuilder add(String path, String value) {
+        return add(path, toJsonString(value));
+    }
+
+    @Override
+    public JsonPatchBuilder add(String path, int value) {
+        return add(path, toJsonNumber(value));
+    }
+
+    @Override
+    public JsonPatchBuilder add(String path, boolean value) {
+        return add(path, toJsonBoolean(value));
+    }
+
+
+    @Override
+    public JsonPatchBuilder remove(String path) {
+        return addOperation(new JsonPatchImpl.PatchValue(JsonPatch.Operation.REMOVE,
+                                                         path,
+                                                         null,
+                                                         null));
+    }
+
+
+    @Override
+    public JsonPatchBuilder replace(String path, JsonValue value) {
+        return addOperation(new JsonPatchImpl.PatchValue(JsonPatch.Operation.REPLACE,
+                                                         path,
+                                                         null,
+                                                         value));
+    }
+
+    @Override
+    public JsonPatchBuilder replace(String path, String value) {
+        return replace(path, toJsonString(value));
+    }
+
+    @Override
+    public JsonPatchBuilder replace(String path, int value) {
+        return replace(path, toJsonNumber(value));
+    }
+
+    @Override
+    public JsonPatchBuilder replace(String path, boolean value) {
+        return replace(path, toJsonBoolean(value));
+    }
+
+
+    @Override
+    public JsonPatchBuilder move(String path, String from) {
+        return addOperation(new JsonPatchImpl.PatchValue(JsonPatch.Operation.MOVE,
+                                                         path,
+                                                         from,
+                                                         null));
+    }
+
+
+    @Override
+    public JsonPatchBuilder copy(String path, String from) {
+        return addOperation(new JsonPatchImpl.PatchValue(JsonPatch.Operation.COPY,
+                                                         path,
+                                                         from,
+                                                         null));
+    }
+
+
+    @Override
+    public JsonPatchBuilder test(String path, JsonValue value) {
+        return addOperation(new JsonPatchImpl.PatchValue(JsonPatch.Operation.TEST,
+                                                         path,
+                                                         null,
+                                                         value));
+    }
+
+    @Override
+    public JsonPatchBuilder test(String path, String value) {
+        return test(path, toJsonString(value));
+    }
+
+    @Override
+    public JsonPatchBuilder test(String path, int value) {
+        return test(path, toJsonNumber(value));
+    }
+
+    @Override
+    public JsonPatchBuilder test(String path, boolean value) {
+        return test(path, toJsonBoolean(value));
+    }
+
+
+    @Override
+    public JsonPatch build() {
+        JsonPatchImpl patch = new JsonPatchImpl(new ArrayList<>(operations));
+
+        return patch;
+
+    }
+
+
+    private JsonPatchBuilder addOperation(JsonPatchImpl.PatchValue operation) {
+        operations.add(operation);
+        return this;
+    }
+
+    private static JsonValue toJsonBoolean(boolean value) {
+        return value ? JsonValue.TRUE : JsonValue.FALSE;
+    }
+
+    private static JsonValue toJsonString(String value) {
+        return value == null ? JsonValue.NULL : new JsonStringImpl(value);
+    }
+
+    private static JsonValue toJsonNumber(int value) {
+        return new JsonLongImpl(value);
+    }
+
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonPatchDiff.java b/src/main/java/org/apache/johnzon/core/JsonPatchDiff.java
new file mode 100644
index 0000000..301a775
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonPatchDiff.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.johnzon.core;
+
+import java.util.Map;
+
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import javax.json.JsonPatch;
+import javax.json.JsonPatchBuilder;
+import javax.json.JsonStructure;
+import javax.json.JsonValue;
+
+/**
+ * Create a diff from a source and target JsonStructure
+ */
+class JsonPatchDiff extends DiffBase {
+
+    private final JsonStructure source;
+    private final JsonStructure target;
+
+    JsonPatchDiff(JsonStructure source, JsonStructure target) {
+        this.source = source;
+        this.target = target;
+    }
+
+    JsonPatch calculateDiff() {
+        JsonPatchBuilder patchBuilder = new JsonPatchBuilderImpl();
+
+        diff(patchBuilder, "", source, target);
+
+        return patchBuilder.build();
+    }
+
+    private void diff(JsonPatchBuilder patchBuilder, String basePath, JsonValue source, JsonValue target) {
+        if (isJsonObject(source) && isJsonObject(target)) {
+            diffJsonObjects(patchBuilder, basePath + "/", (JsonObject) source, (JsonObject) target);
+        } else if (isJsonArray(source) && isJsonArray(target)) {
+            diffJsonArray(patchBuilder, basePath + "/", (JsonArray) source, (JsonArray) target);
+        } else if (!source.equals(target)){
+            patchBuilder.replace(basePath, target);
+        }
+    }
+
+    private void diffJsonArray(JsonPatchBuilder patchBuilder, String basePath, JsonArray source, JsonArray target) {
+        for (int i = 0; i < source.size(); i++) {
+            JsonValue sourceValue = source.get(i);
+
+            if (target.size() <= i) {
+                patchBuilder.remove(basePath + i);
+                continue;
+            }
+
+            diff(patchBuilder, basePath + i, sourceValue, target.get(i));
+        }
+
+        if (target.size() > source.size()) {
+
+            for (int i = target.size() - source.size(); i < target.size(); i++) {
+                patchBuilder.add(basePath + i, target.get(i));
+            }
+        }
+
+    }
+
+    private void diffJsonObjects(JsonPatchBuilder patchBuilder, String basePath, JsonObject source, JsonObject target) {
+
+        for (Map.Entry<String, JsonValue> sourceEntry : source.entrySet()) {
+            String attributeName = sourceEntry.getKey();
+
+            if (target.containsKey(attributeName)) {
+                diff(patchBuilder, basePath + JsonPointerUtil.encode(attributeName), sourceEntry.getValue(), target.get(attributeName));
+            } else {
+                // the value got removed
+                patchBuilder.remove(basePath + JsonPointerUtil.encode(attributeName));
+            }
+        }
+
+        for (Map.Entry<String, JsonValue> targetEntry : target.entrySet()) {
+            if (!source.containsKey(targetEntry.getKey())) {
+                patchBuilder.add(basePath + JsonPointerUtil.encode(targetEntry.getKey()), targetEntry.getValue());
+            }
+        }
+
+    }
+
+
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonPatchImpl.java b/src/main/java/org/apache/johnzon/core/JsonPatchImpl.java
new file mode 100644
index 0000000..257ab40
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonPatchImpl.java
@@ -0,0 +1,213 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonException;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonPatch;
+import javax.json.JsonStructure;
+import javax.json.JsonValue;
+
+class JsonPatchImpl implements JsonPatch {
+
+    private final List<PatchValue> patches;
+
+
+    JsonPatchImpl(PatchValue... patches) {
+        this.patches = Arrays.asList(patches);
+    }
+
+    JsonPatchImpl(List<PatchValue> patches) {
+        if (patches == null) {
+            this.patches = Collections.emptyList();
+        } else {
+            this.patches = Collections.unmodifiableList(patches);
+        }
+    }
+
+
+    @Override
+    public <T extends JsonStructure> T apply(T target) {
+
+        //X TODO JsonPointer should use generics like JsonPatch
+        JsonStructure patched = target;
+
+        for (PatchValue patch : patches) {
+
+            switch (patch.operation) {
+                case ADD:
+                    patched = patch.path.add(patched, patch.value);
+                    break;
+                case REMOVE:
+                    patched = patch.path.remove(patched);
+                    break;
+                case REPLACE:
+                    // first remove the existing element and then add the new value
+                    patched = patch.path.add(patch.path.remove(patched), patch.value);
+                    break;
+                case MOVE:
+                    JsonValue valueToMove = patch.from.getValue(patched);
+                    patched = patch.path.add(patch.from.remove(patched), valueToMove);
+                    break;
+                case COPY:
+                    JsonValue toCopy = patch.from.getValue(patched);
+                    patched = patch.path.add(patched, toCopy);
+                    break;
+                case TEST:
+                    JsonValue toTest = patch.path.getValue(patched);
+                    if (!toTest.equals(patch.value)) {
+                        throw new JsonException("JsonPatch.Operation.TEST fails! Values are not equal");
+                    }
+                    break;
+                default:
+                    throw new IllegalStateException("unsupported operation: " + patch.operation);
+            }
+        }
+
+        //X TODO dirty cast can be removed after JsonPointer uses generics like JsonPatch
+        return (T) patched;
+    }
+
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        JsonPatchImpl jsonPatch = (JsonPatchImpl) o;
+
+        return patches.equals(jsonPatch.patches);
+    }
+
+    @Override
+    public int hashCode() {
+        return patches.hashCode();
+    }
+
+
+    @Override
+    public JsonArray toJsonArray() {
+
+        JsonArrayBuilder builder = Json.createArrayBuilder();
+        for (PatchValue patch : patches) {
+            builder.add(patch.toJson());
+        }
+
+        return builder.build();
+    }
+
+
+
+    static class PatchValue {
+        private final JsonPatch.Operation operation;
+        private final JsonPointerImpl path;
+        private final JsonPointerImpl from;
+        private final JsonValue value;
+
+        PatchValue(JsonPatch.Operation operation,
+                   String path,
+                   String from,
+                   JsonValue value) {
+            this.operation = operation;
+            this.path = new JsonPointerImpl(path);
+
+            // ignore from if we do not need it
+            if (operation == JsonPatch.Operation.MOVE || operation == JsonPatch.Operation.COPY) {
+                this.from = new JsonPointerImpl(from);
+            } else {
+                this.from = null;
+            }
+
+            this.value = value;
+        }
+
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            PatchValue that = (PatchValue) o;
+
+            if (operation != that.operation) {
+                return false;
+            }
+            if (!path.equals(that.path)) {
+                return false;
+            }
+            if (from != null ? !from.equals(that.from) : that.from != null) {
+                return false;
+            }
+            return value != null ? value.equals(that.value) : that.value == null;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = operation.hashCode();
+            result = 31 * result + path.hashCode();
+            result = 31 * result + (from != null ? from.hashCode() : 0);
+            result = 31 * result + (value != null ? value.hashCode() : 0);
+            return result;
+        }
+
+
+        @Override
+        public String toString() {
+            return "{" +
+                   "op: " + operation +
+                   ", path: " + path +
+                   ", from: " + from +
+                   ", value: " + value +
+                   '}';
+        }
+
+        JsonObject toJson() {
+            JsonObjectBuilder builder = Json.createObjectBuilder()
+                                            .add("op", operation.name().toLowerCase())
+                                            .add("path", path.getJsonPointer());
+
+            if (from != null) {
+                builder.add("from", from.getJsonPointer());
+            }
+
+            if (value != null) {
+                builder.add("value", value);
+            }
+
+            return builder.build();
+        }
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonPointerImpl.java b/src/main/java/org/apache/johnzon/core/JsonPointerImpl.java
new file mode 100644
index 0000000..b674ad9
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonPointerImpl.java
@@ -0,0 +1,484 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonException;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonPointer;
+import javax.json.JsonStructure;
+import javax.json.JsonValue;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+
+public class JsonPointerImpl implements JsonPointer {
+
+    private final String jsonPointer;
+    private final List<String> referenceTokens = new ArrayList<>();
+    private final String lastReferenceToken;
+
+    /**
+     * Constructs and initializes a JsonPointer.
+     *
+     * @param jsonPointer the JSON Pointer string
+     * @throws NullPointerException if {@code jsonPointer} is {@code null}
+     * @throws JsonException        if {@code jsonPointer} is not a valid JSON Pointer
+     */
+    public JsonPointerImpl(String jsonPointer) {
+        if (jsonPointer == null) {
+            throw new NullPointerException("jsonPointer must not be null");
+        }
+        if (!jsonPointer.equals("") && !jsonPointer.startsWith("/")) {
+            throw new JsonException("A non-empty JsonPointer string must begin with a '/'");
+        }
+
+        this.jsonPointer = jsonPointer;
+        String[] encodedReferenceTokens = jsonPointer.split("/", -1);
+
+        for (String encodedReferenceToken : encodedReferenceTokens) {
+            referenceTokens.add(JsonPointerUtil.decode(encodedReferenceToken));
+        }
+        lastReferenceToken = referenceTokens.get(referenceTokens.size() - 1);
+    }
+
+    /**
+     * Compares this {@code JsonPointer} with another object.
+     *
+     * @param obj the object to compare this {@code JsonPointer} against
+     * @return true if the given object is a {@code JsonPointer} with the same
+     * reference tokens as this one, false otherwise.
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+
+        JsonPointerImpl that = (JsonPointerImpl) obj;
+        return jsonPointer.equals(that.jsonPointer);
+    }
+
+    /**
+     * Returns the hash code value for this {@code JsonPointer} object.
+     * The hash code of this object is defined by the hash codes of it's reference tokens.
+     *
+     * @return the hash code value for this {@code JsonPointer} object
+     */
+    @Override
+    public int hashCode() {
+        return jsonPointer.hashCode();
+    }
+
+    /**
+     * Returns the value at the referenced location in the specified {@code target}
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @return the referenced value in the target.
+     * @throws NullPointerException if {@code target} is null
+     * @throws JsonException        if the referenced value does not exist
+     */
+    public JsonValue getValue(JsonStructure target) {
+        if (target == null) {
+            throw new NullPointerException("target must not be null");
+        }
+        if (isEmptyJsonPointer()) {
+            return target;
+        }
+
+        JsonValue jsonValue = target;
+        for (int i = 1; i < referenceTokens.size(); i++) {
+            jsonValue = getValue(jsonValue, referenceTokens.get(i), i, referenceTokens.size() - 1);
+        }
+        return jsonValue;
+    }
+
+    @Override
+    public boolean containsValue(JsonStructure target) {
+        try {
+            getValue(target);
+            return true;
+        } catch (JsonException je) {
+            return false;
+        }
+    }
+
+    /**
+     * Adds or replaces a value at the referenced location in the specified
+     * {@code target} with the specified {@code value}.
+     * <ol>
+     * <li>If the reference is the target (empty JSON Pointer string),
+     * the specified {@code value}, which must be the same type as
+     * specified {@code target}, is returned.</li>
+     * <li>If the reference is an array element, the specified {@code value} is inserted
+     * into the array, at the referenced index. The value currently at that location, and
+     * any subsequent values, are shifted to the right (adds one to the indices).
+     * Index starts with 0. If the reference is specified with a "-", or if the
+     * index is equal to the size of the array, the value is appended to the array.</li>
+     * <li>If the reference is a name/value pair of a {@code JsonObject}, and the
+     * referenced value exists, the value is replaced by the specified {@code value}.
+     * If the value does not exist, a new name/value pair is added to the object.</li>
+     * </ol>
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @param value  the value to be added
+     * @return the transformed {@code target} after the value is added.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException        if the reference is an array element and
+     *                              the index is out of range ({@code index < 0 || index > array size}),
+     *                              or if the pointer contains references to non-existing objects or arrays.
+     */
+    public JsonStructure add(JsonStructure target, JsonValue value) {
+        validateAdd(target);
+        if (isEmptyJsonPointer()) {
+            if (value.getClass() != target.getClass()) {
+                throw new JsonException("The value must have the same type as the target");
+            }
+            return (JsonStructure) value;
+        }
+
+        return addInternal(target, value);
+    }
+
+    /**
+     * Adds or replaces a value at the referenced location in the specified
+     * {@code target} with the specified {@code value}.
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @param value  the value to be added
+     * @return the transformed {@code target} after the value is added.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException        if the reference is an array element and
+     *                              the index is out of range ({@code index < 0 || index > array size}),
+     *                              or if the pointer contains references to non-existing objects or arrays.
+     * @see #add(JsonStructure, JsonValue)
+     */
+    public JsonObject add(JsonObject target, JsonValue value) {
+        validateAdd(target);
+
+        return addInternal(target, value);
+    }
+
+    /**
+     * Adds or replaces a value at the referenced location in the specified
+     * {@code target} with the specified {@code value}.
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @param value  the value to be added
+     * @return the transformed {@code target} after the value is added.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException        if the reference is an array element and
+     *                              the index is out of range ({@code index < 0 || index > array size}),
+     *                              or if the pointer contains references to non-existing objects or arrays.
+     * @see #add(JsonStructure, JsonValue)
+     */
+    public JsonArray add(JsonArray target, JsonValue value) {
+        validateAdd(target);
+
+        return addInternal(target, value);
+    }
+
+    /**
+     * Replaces the value at the referenced location in the specified
+     * {@code target} with the specified {@code value}.
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @param value  the value to be stored at the referenced location
+     * @return the transformed {@code target} after the value is replaced.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException        if the referenced value does not exist,
+     *                              or if the reference is the target.
+     */
+    public JsonStructure replace(JsonStructure target, JsonValue value) {
+        if (target instanceof JsonObject) {
+            return replace((JsonObject) target, value);
+        } else {
+            return replace((JsonArray) target, value);
+        }
+    }
+
+    /**
+     * Replaces the value at the referenced location in the specified
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @param value  the value to be stored at the referenced location
+     * @return the transformed {@code target} after the value is replaced.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException        if the referenced value does not exist,
+     *                              or if the reference is the target.
+     * @see #replace(JsonStructure, JsonValue)
+     */
+    public JsonObject replace(JsonObject target, JsonValue value) {
+        return add(remove(target), value);
+    }
+
+    /**
+     * Replaces the value at the referenced location in the specified
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @param value  the value to be stored at the referenced location
+     * @return the transformed {@code target} after the value is replaced.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException        if the referenced value does not exist,
+     *                              or if the reference is the target.
+     * @see #replace(JsonStructure, JsonValue)
+     */
+    public JsonArray replace(JsonArray target, JsonValue value) {
+        return add(remove(target), value);
+    }
+
+    /**
+     * Removes the value at the reference location in the specified {@code target}
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @return the transformed {@code target} after the value is removed.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException        if the referenced value does not exist,
+     *                              or if the reference is the target.
+     */
+    public JsonStructure remove(JsonStructure target) {
+        if (target instanceof JsonObject) {
+            return remove((JsonObject) target);
+        } else {
+            return remove((JsonArray) target);
+        }
+    }
+
+    /**
+     * Removes the value at the reference location in the specified {@code target}
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @return the transformed {@code target} after the value is removed.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException        if the referenced value does not exist,
+     *                              or if the reference is the target.
+     * @see #remove(JsonStructure)
+     */
+    public JsonObject remove(JsonObject target) {
+        validateRemove(target);
+
+        return (JsonObject) remove(target, 1, referenceTokens.size() - 1);
+    }
+
+    /**
+     * Removes the value at the reference location in the specified {@code target}
+     *
+     * @param target the target referenced by this {@code JsonPointer}
+     * @return the transformed {@code target} after the value is removed.
+     * @throws NullPointerException if {@code target} is {@code null}
+     * @throws JsonException        if the referenced value does not exist,
+     *                              or if the reference is the target.
+     * @see #remove(JsonStructure)
+     */
+    public JsonArray remove(JsonArray target) {
+        validateRemove(target);
+
+        return (JsonArray) remove(target, 1, referenceTokens.size() - 1);
+    }
+
+    String getJsonPointer() {
+        return jsonPointer;
+    }
+
+    private void validateAdd(JsonValue target) {
+        validateJsonPointer(target, referenceTokens.size() - 1);
+    }
+
+    private void validateRemove(JsonValue target) {
+        validateJsonPointer(target, referenceTokens.size());
+        if (isEmptyJsonPointer()) {
+            throw new JsonException("The reference must not be the target");
+        }
+    }
+
+    private boolean isEmptyJsonPointer() {
+        return jsonPointer.equals("");
+    }
+
+    private JsonValue getValue(JsonValue jsonValue, String referenceToken, int currentPosition, int referencePosition) {
+        if (jsonValue instanceof JsonObject) {
+            JsonObject jsonObject = (JsonObject) jsonValue;
+            jsonValue = jsonObject.get(referenceToken);
+
+            if (jsonValue != null) {
+                return jsonValue;
+            }
+            throw new JsonException("'" + jsonObject + "' contains no value for name '" + referenceToken + "'");
+        } else if (jsonValue instanceof JsonArray) {
+            validateArrayIndex(referenceToken);
+
+            try {
+                JsonArray jsonArray = (JsonArray) jsonValue;
+                int arrayIndex = Integer.parseInt(referenceToken);
+                validateArraySize(jsonArray, arrayIndex, jsonArray.size());
+                return jsonArray.get(arrayIndex);
+            } catch (NumberFormatException e) {
+                throw new JsonException("'" + referenceToken + "' is no valid array index", e);
+            }
+        } else {
+            if (currentPosition != referencePosition) {
+                return jsonValue;
+            }
+            throw new JsonException("'" + jsonValue + "' contains no element for '" + referenceToken + "'");
+        }
+    }
+
+    private <T extends JsonStructure> T addInternal(T jsonValue, JsonValue newValue) {
+        List<String> currentPath = new ArrayList<>();
+        currentPath.add("");
+
+        return (T) addInternal(jsonValue, newValue, currentPath);
+    }
+
+    private JsonValue addInternal(JsonValue jsonValue, JsonValue newValue, List<String> currentPath) {
+        if (jsonValue instanceof JsonObject) {
+            JsonObject jsonObject = (JsonObject) jsonValue;
+            JsonObjectBuilder objectBuilder = Json.createObjectBuilder();
+
+            if (jsonObject.isEmpty()) {
+                objectBuilder.add(lastReferenceToken, newValue);
+            } else {
+                for (Map.Entry<String, JsonValue> entry : jsonObject.entrySet()) {
+
+                    currentPath.add(entry.getKey());
+                    objectBuilder.add(entry.getKey(), addInternal(entry.getValue(), newValue, currentPath));
+                    currentPath.remove(entry.getKey());
+
+                    if (currentPath.size() == referenceTokens.size() - 1 &&
+                        currentPath.get(currentPath.size() - 1).equals(referenceTokens.get(referenceTokens.size() - 2))) {
+                        objectBuilder.add(lastReferenceToken, newValue);
+                    }
+                }
+            }
+            return objectBuilder.build();
+        } else if (jsonValue instanceof JsonArray) {
+            JsonArray jsonArray = (JsonArray) jsonValue;
+            JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
+
+            int arrayIndex = -1;
+            if (currentPath.size() == referenceTokens.size() - 1 &&
+                currentPath.get(currentPath.size() - 1).equals(referenceTokens.get(referenceTokens.size() - 2))) {
+
+                arrayIndex = getArrayIndex(lastReferenceToken, jsonArray, true);
+            }
+
+            int jsonArraySize = jsonArray.size();
+            for (int i = 0; i <= jsonArraySize; i++) {
+                if (i == arrayIndex) {
+                    arrayBuilder.add(newValue);
+                }
+                if (i == jsonArraySize) {
+                    break;
+                }
+
+                String path = String.valueOf(i);
+                currentPath.add(path);
+                arrayBuilder.add(addInternal(jsonArray.get(i), newValue, currentPath));
+                currentPath.remove(path);
+            }
+            return arrayBuilder.build();
+        }
+        return jsonValue;
+    }
+
+    private JsonValue remove(JsonValue jsonValue, int currentPosition, int referencePosition) {
+        if (jsonValue instanceof JsonObject) {
+            JsonObject jsonObject = (JsonObject) jsonValue;
+            JsonObjectBuilder objectBuilder = Json.createObjectBuilder();
+
+            for (Map.Entry<String, JsonValue> entry : jsonObject.entrySet()) {
+                if (currentPosition == referencePosition
+                        && lastReferenceToken.equals(entry.getKey())) {
+                    continue;
+                }
+                objectBuilder.add(entry.getKey(), remove(entry.getValue(), currentPosition + 1, referencePosition));
+            }
+            return objectBuilder.build();
+        } else if (jsonValue instanceof JsonArray) {
+            JsonArray jsonArray = (JsonArray) jsonValue;
+            JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
+
+            int arrayIndex = -1;
+            if (currentPosition == referencePosition) {
+                arrayIndex = getArrayIndex(lastReferenceToken, jsonArray, false);
+            }
+
+            int jsonArraySize = jsonArray.size();
+            for (int i = 0; i < jsonArraySize; i++) {
+                if (i == arrayIndex) {
+                    continue;
+                }
+                arrayBuilder.add(remove(jsonArray.get(i), currentPosition + 1, referencePosition));
+            }
+            return arrayBuilder.build();
+        }
+        return jsonValue;
+    }
+
+    private int getArrayIndex(String referenceToken, JsonArray jsonArray, boolean addOperation) {
+        if (addOperation && referenceToken.equals("-")) {
+            return jsonArray.size();
+        }
+
+        validateArrayIndex(referenceToken);
+
+        try {
+            int arrayIndex = Integer.parseInt(referenceToken);
+            int arraySize = addOperation ? jsonArray.size() + 1 : jsonArray.size();
+            validateArraySize(jsonArray, arrayIndex, arraySize);
+            return arrayIndex;
+        } catch (NumberFormatException e) {
+            throw new JsonException("'" + referenceToken + "' is no valid array index", e);
+        }
+    }
+
+    private void validateJsonPointer(JsonValue target, int size) throws NullPointerException, JsonException {
+        if (target == null) {
+            throw new NullPointerException("target must not be null");
+        }
+
+        JsonValue jsonValue = target;
+        for (int i = 1; i < size; i++) {
+            jsonValue = getValue(jsonValue, referenceTokens.get(i), i, referenceTokens.size() - 1);
+        }
+    }
+
+    private void validateArrayIndex(String referenceToken) throws JsonException {
+        if (referenceToken.startsWith("+") || referenceToken.startsWith("-")) {
+            throw new JsonException("An array index must not start with '" + referenceToken.charAt(0) + "'");
+        }
+        if (referenceToken.startsWith("0") && referenceToken.length() > 1) {
+            throw new JsonException("An array index must not start with a leading '0'");
+        }
+    }
+
+    private void validateArraySize(JsonArray jsonArray, int arrayIndex, int arraySize) throws JsonException {
+        if (arrayIndex >= arraySize) {
+            throw new JsonException("'" + jsonArray + "' contains no element for index " + arrayIndex);
+        }
+    }
+
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonPointerUtil.java b/src/main/java/org/apache/johnzon/core/JsonPointerUtil.java
new file mode 100644
index 0000000..91bc9c2
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonPointerUtil.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+public class JsonPointerUtil {
+
+    private JsonPointerUtil() {
+
+    }
+
+    /**
+     * Transforms "~" to "~0" and then "/" to "~1"
+     */
+    public static String encode(String s) {
+        if (s == null || s.length() == 0) {
+            return s;
+        }
+
+        return s.replace("~", "~0").replace("/", "~1");
+    }
+
+    /**
+     * Transforms "~1" to "/" and then "~0" to "~",
+     */
+    public static String decode(String s) {
+        if (s == null || s.length() == 0) {
+            return s;
+        }
+
+        return s.replace("~1", "/").replace("~0", "~");
+    }
+
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonProviderImpl.java b/src/main/java/org/apache/johnzon/core/JsonProviderImpl.java
new file mode 100644
index 0000000..afc1da2
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonProviderImpl.java
@@ -0,0 +1,384 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import org.osgi.annotation.versioning.ProviderType;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Serializable;
+import java.io.Writer;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Collection;
+import java.util.Map;
+
+import javax.json.JsonArray;
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonBuilderFactory;
+import javax.json.JsonMergePatch;
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonPatch;
+import javax.json.JsonPatchBuilder;
+import javax.json.JsonPointer;
+import javax.json.JsonReader;
+import javax.json.JsonReaderFactory;
+import javax.json.JsonString;
+import javax.json.JsonStructure;
+import javax.json.JsonValue;
+import javax.json.JsonWriter;
+import javax.json.JsonWriterFactory;
+import javax.json.spi.JsonProvider;
+import javax.json.stream.JsonGenerator;
+import javax.json.stream.JsonGeneratorFactory;
+import javax.json.stream.JsonParser;
+import javax.json.stream.JsonParserFactory;
+
+@ProviderType
+public class JsonProviderImpl extends JsonProvider implements Serializable {
+    private static final JsonProvider DELEGATE = new JsonProviderDelegate();
+
+    @Override
+    public JsonParser createParser(final Reader reader) {
+        return DELEGATE.createParser(reader);
+    }
+
+    @Override
+    public JsonParser createParser(final InputStream inputStream) {
+        return DELEGATE.createParser(inputStream);
+    }
+
+    @Override
+    public JsonParserFactory createParserFactory(final Map<String, ?> stringMap) {
+        return DELEGATE.createParserFactory(stringMap);
+    }
+
+    @Override
+    public JsonGenerator createGenerator(final Writer writer) {
+        return DELEGATE.createGenerator(writer);
+    }
+
+    @Override
+    public JsonGenerator createGenerator(final OutputStream outputStream) {
+        return DELEGATE.createGenerator(outputStream);
+    }
+
+    @Override
+    public JsonGeneratorFactory createGeneratorFactory(final Map<String, ?> stringMap) {
+        return DELEGATE.createGeneratorFactory(stringMap);
+    }
+
+    @Override
+    public JsonReader createReader(final Reader reader) {
+        return DELEGATE.createReader(reader);
+    }
+
+    @Override
+    public JsonReader createReader(final InputStream inputStream) {
+        return DELEGATE.createReader(inputStream);
+    }
+
+    @Override
+    public JsonWriter createWriter(final Writer writer) {
+        return DELEGATE.createWriter(writer);
+    }
+
+    @Override
+    public JsonWriter createWriter(final OutputStream outputStream) {
+        return DELEGATE.createWriter(outputStream);
+    }
+
+    @Override
+    public JsonWriterFactory createWriterFactory(final Map<String, ?> stringMap) {
+        return DELEGATE.createWriterFactory(stringMap);
+    }
+
+    @Override
+    public JsonReaderFactory createReaderFactory(final Map<String, ?> stringMap) {
+        return DELEGATE.createReaderFactory(stringMap);
+    }
+
+    @Override
+    public JsonObjectBuilder createObjectBuilder() {
+        return DELEGATE.createObjectBuilder();
+    }
+
+    @Override
+    public JsonArrayBuilder createArrayBuilder() {
+        return DELEGATE.createArrayBuilder();
+    }
+
+    @Override
+    public JsonBuilderFactory createBuilderFactory(final Map<String, ?> stringMap) {
+        return DELEGATE.createBuilderFactory(stringMap);
+    }
+    
+    @Override
+    public JsonPatchBuilder createPatchBuilder() {
+        return DELEGATE.createPatchBuilder();
+    }
+
+    @Override
+    public JsonPatchBuilder createPatchBuilder(JsonArray initialData) {
+        return DELEGATE.createPatchBuilder(initialData);
+    }
+
+    @Override
+    public JsonObjectBuilder createObjectBuilder(JsonObject jsonObject) {
+        return DELEGATE.createObjectBuilder(jsonObject);
+    }
+
+    @Override
+    public JsonObjectBuilder createObjectBuilder(Map<String, Object> map) {
+        return DELEGATE.createObjectBuilder(map);
+    }
+
+    @Override
+    public JsonArrayBuilder createArrayBuilder(JsonArray initialData) {
+        return DELEGATE.createArrayBuilder(initialData);
+    }
+
+    @Override
+    public JsonArrayBuilder createArrayBuilder(Collection<?> initialData) {
+        return DELEGATE.createArrayBuilder(initialData);
+    }
+
+    @Override
+    public JsonPointer createPointer(String path) {
+        return DELEGATE.createPointer(path);
+    }
+
+    @Override
+    public JsonString createValue(String value) {
+        return DELEGATE.createValue(value);
+    }
+
+    @Override
+    public JsonNumber createValue(int value) {
+        return DELEGATE.createValue(value);
+    }
+
+    @Override
+    public JsonNumber createValue(long value) {
+        return DELEGATE.createValue(value);
+    }
+
+    @Override
+    public JsonNumber createValue(double value) {
+        return DELEGATE.createValue(value);
+    }
+
+    @Override
+    public JsonNumber createValue(BigDecimal value) {
+        return DELEGATE.createValue(value);
+    }
+
+    @Override
+    public JsonNumber createValue(BigInteger value) {
+        return DELEGATE.createValue(value);
+    }
+
+    @Override
+    public JsonPatch createPatch(JsonArray array) {
+        return DELEGATE.createPatch(array);
+    }
+
+    @Override
+    public JsonPatch createDiff(JsonStructure source, JsonStructure target) {
+        return DELEGATE.createDiff(source, target);
+    }
+
+    @Override
+    public JsonMergePatch createMergePatch(JsonValue patch) {
+        return DELEGATE.createMergePatch(patch);
+    }
+
+    @Override
+    public JsonMergePatch createMergeDiff(JsonValue source, JsonValue target) {
+        return DELEGATE.createMergeDiff(source, target);
+    }
+
+    static class JsonProviderDelegate extends JsonProvider {
+        private final JsonReaderFactory readerFactory = new JsonReaderFactoryImpl(null);
+        private final JsonParserFactory parserFactory = new JsonParserFactoryImpl(null);
+        private final JsonGeneratorFactory generatorFactory = new JsonGeneratorFactoryImpl(null);
+        private final JsonWriterFactory writerFactory = new JsonWriterFactoryImpl(null);
+        private final JsonBuilderFactoryImpl builderFactory = new JsonBuilderFactoryImpl(null);
+
+        @Override
+        public JsonParser createParser(final InputStream in) {
+            return parserFactory.createParser(in);
+        }
+
+        @Override
+        public JsonParser createParser(final Reader reader) {
+            return parserFactory.createParser(reader);
+        }
+
+        @Override
+        public JsonReader createReader(final InputStream in) {
+            return readerFactory.createReader(in);
+        }
+
+        @Override
+        public JsonReader createReader(final Reader reader) {
+            return readerFactory.createReader(reader);
+        }
+
+        @Override
+        public JsonParserFactory createParserFactory(final Map<String, ?> config) {
+            return (config == null || config.isEmpty()) ? parserFactory : new JsonParserFactoryImpl(config);
+        }
+
+        @Override
+        public JsonReaderFactory createReaderFactory(final Map<String, ?> config) {
+            return (config == null || config.isEmpty()) ? readerFactory : new JsonReaderFactoryImpl(config);
+        }
+
+        @Override
+        public JsonGenerator createGenerator(final Writer writer) {
+            return generatorFactory.createGenerator(writer);
+        }
+
+        @Override
+        public JsonGenerator createGenerator(final OutputStream out) {
+            return generatorFactory.createGenerator(out);
+        }
+
+        @Override
+        public JsonGeneratorFactory createGeneratorFactory(final Map<String, ?> config) {
+            return (config == null || config.isEmpty()) ? generatorFactory : new JsonGeneratorFactoryImpl(config);
+        }
+
+        @Override
+        public JsonWriter createWriter(final Writer writer) {
+            return writerFactory.createWriter(writer);
+        }
+
+        @Override
+        public JsonWriter createWriter(final OutputStream out) {
+            return writerFactory.createWriter(out);
+        }
+
+        @Override
+        public JsonWriterFactory createWriterFactory(final Map<String, ?> config) {
+            return (config == null || config.isEmpty()) ? writerFactory : new JsonWriterFactoryImpl(config);
+        }
+
+        @Override
+        public JsonObjectBuilder createObjectBuilder() {
+            return builderFactory.createObjectBuilder();
+        }
+
+        @Override
+        public JsonObjectBuilder createObjectBuilder(JsonObject jsonObject) {
+            return builderFactory.createObjectBuilder(jsonObject);
+        }
+
+        @Override
+        public JsonObjectBuilder createObjectBuilder(Map<String, Object> initialValues) {
+            return builderFactory.createObjectBuilder(initialValues);
+        }
+
+        @Override
+        public JsonArrayBuilder createArrayBuilder() {
+            return builderFactory.createArrayBuilder();
+        }
+
+        @Override
+        public JsonArrayBuilder createArrayBuilder(JsonArray initialData) {
+            return builderFactory.createArrayBuilder(initialData);
+        }
+
+        public JsonArrayBuilder createArrayBuilder(Collection<?> initialData) {
+            return builderFactory.createArrayBuilder(initialData);
+        }
+
+        @Override
+        public JsonString createValue(String value) {
+            return new JsonStringImpl(value);
+        }
+
+        @Override
+        public JsonNumber createValue(int value) {
+            return new JsonLongImpl(value);
+        }
+
+        @Override
+        public JsonNumber createValue(long value) {
+            return new JsonLongImpl(value);
+        }
+
+        @Override
+        public JsonNumber createValue(double value) {
+            return new JsonDoubleImpl(value);
+        }
+
+        @Override
+        public JsonNumber createValue(BigDecimal value) {
+            return new JsonNumberImpl(value);
+        }
+
+        @Override
+        public JsonNumber createValue(BigInteger value) {
+            return new JsonLongImpl(value.longValue());
+        }
+
+        @Override
+        public JsonBuilderFactory createBuilderFactory(final Map<String, ?> config) {
+            return (config == null || config.isEmpty()) ? builderFactory : new JsonBuilderFactoryImpl(config);
+        }
+
+        @Override
+        public JsonPatchBuilder createPatchBuilder() {
+            return new JsonPatchBuilderImpl();
+        }
+
+        @Override
+        public JsonPatchBuilder createPatchBuilder(JsonArray initialData) {
+            return new JsonPatchBuilderImpl(initialData);
+        }
+
+        @Override
+        public JsonPointer createPointer(String path) {
+            return new JsonPointerImpl(path);
+        }
+
+        public JsonPatch createPatch(JsonArray array) {
+            return createPatchBuilder(array).build();
+        }
+
+        @Override
+        public JsonPatch createDiff(JsonStructure source, JsonStructure target) {
+            return new JsonPatchDiff(source, target).calculateDiff();
+        }
+
+        public JsonMergePatch createMergePatch(JsonValue patch) {
+            return new JsonMergePatchImpl(patch);
+        }
+
+        @Override
+        public JsonMergePatch createMergeDiff(JsonValue source, JsonValue target) {
+            return new JsonMergePatchDiff(source, target).calculateDiff();
+        }
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonReaderFactoryImpl.java b/src/main/java/org/apache/johnzon/core/JsonReaderFactoryImpl.java
new file mode 100644
index 0000000..98484b1
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonReaderFactoryImpl.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import static java.util.Arrays.asList;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.nio.charset.Charset;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+import javax.json.JsonReader;
+import javax.json.JsonReaderFactory;
+
+class JsonReaderFactoryImpl extends AbstractJsonFactory implements JsonReaderFactory {
+    static final Collection<String> SUPPORTED_CONFIG_KEYS = asList(
+
+    );
+    private final JsonParserFactoryImpl parserFactory;
+
+    JsonReaderFactoryImpl(final Map<String, ?> config) {
+        super(config, SUPPORTED_CONFIG_KEYS, JsonParserFactoryImpl.SUPPORTED_CONFIG_KEYS);
+        this.parserFactory = new JsonParserFactoryImpl(internalConfig);
+    }
+
+    @Override
+    public JsonReader createReader(final Reader reader) {
+        return new JsonReaderImpl(parserFactory.createInternalParser(reader));
+    }
+
+    @Override
+    public JsonReader createReader(final InputStream in) {
+        return new JsonReaderImpl(parserFactory.createInternalParser(in));
+    }
+
+    @Override
+    public JsonReader createReader(final InputStream in, final Charset charset) {
+        return new JsonReaderImpl(parserFactory.createInternalParser(in, charset));
+    }
+
+    @Override
+    public Map<String, ?> getConfigInUse() {
+        return Collections.unmodifiableMap(internalConfig);
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonReaderImpl.java b/src/main/java/org/apache/johnzon/core/JsonReaderImpl.java
new file mode 100644
index 0000000..e3af4af
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonReaderImpl.java
@@ -0,0 +1,261 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+
+import javax.json.JsonArray;
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonReader;
+import javax.json.JsonStructure;
+import javax.json.JsonValue;
+import javax.json.stream.JsonParser;
+import javax.json.stream.JsonParsingException;
+
+public class JsonReaderImpl implements JsonReader {
+    private final JohnzonJsonParser parser;
+    private boolean closed = false;
+
+    public JsonReaderImpl(final JsonParser parser) {
+        if (parser instanceof JohnzonJsonParser) {
+            this.parser = (JohnzonJsonParser) parser;
+        } else {
+            this.parser = new JohnzonJsonParser.JohnzonJsonParserWrapper(parser);
+        }
+    }
+
+    @Override
+    public JsonStructure read() {
+        return JsonStructure.class.cast(readValue());
+    }
+
+    @Override
+    public JsonValue readValue() {
+        checkClosed();
+
+        if (!parser.hasNext()) {
+            throw new IllegalStateException("Nothing to read");
+        }
+        final JsonParser.Event next = parser.next();
+        switch (next) {
+            case START_OBJECT:
+                final JsonObjectBuilder objectBuilder = new JsonObjectBuilderImpl();
+                parseObject(objectBuilder);
+                if (parser.hasNext()) {
+                    throw new JsonParsingException("Expected end of file", parser.getLocation());
+                }
+                close();
+                return objectBuilder.build();
+            case START_ARRAY:
+                final JsonArrayBuilder arrayBuilder = new JsonArrayBuilderImpl();
+                parseArray(arrayBuilder);
+                if (parser.hasNext()) {
+                    throw new JsonParsingException("Expected end of file", parser.getLocation());
+                }
+                close();
+                return arrayBuilder.build();
+            case VALUE_STRING:
+                if (parser.hasNext()) {
+                    throw new JsonParsingException("Expected end of file", parser.getLocation());
+                }
+                final JsonStringImpl string = new JsonStringImpl(parser.getString());
+                close();
+                return string;
+            case VALUE_FALSE:
+                if (parser.hasNext()) {
+                    throw new JsonParsingException("Expected end of file", parser.getLocation());
+                }
+                close();
+                return JsonValue.FALSE;
+            case VALUE_TRUE:
+                if (parser.hasNext()) {
+                    throw new JsonParsingException("Expected end of file", parser.getLocation());
+                }
+                close();
+                return JsonValue.TRUE;
+            case VALUE_NULL:
+                if (parser.hasNext()) {
+                    throw new JsonParsingException("Expected end of file", parser.getLocation());
+                }
+                close();
+                return JsonValue.NULL;
+            case VALUE_NUMBER:
+                if (parser.hasNext()) {
+                    throw new JsonParsingException("Expected end of file", parser.getLocation());
+                }
+                final JsonNumber number = new JsonNumberImpl(parser.getBigDecimal());
+                close();
+                return number;
+            default:
+                close();
+                throw new JsonParsingException("Unknown structure: " + next, parser.getLocation());
+        }
+    }
+
+    @Override
+    public JsonObject readObject() {
+        final JsonStructure read = read();
+        checkType(JsonObject.class, read);
+        return JsonObject.class.cast(read);
+    }
+
+    @Override
+    public JsonArray readArray() {
+        final JsonStructure read = read();
+        checkType(JsonArray.class, read);
+        return JsonArray.class.cast(read);
+    }
+
+    private void checkType(final Class<?> expected, final JsonStructure read) {
+        if (!expected.isInstance(read)) {
+            throw new JsonParsingException("Expecting " + expected + " but got " + read, parser.getLocation());
+        }
+    }
+
+    @Override
+    public void close() {
+
+        if (!closed) {
+            closed = true;
+            parser.close();
+        }
+
+    }
+
+    private void parseObject(final JsonObjectBuilder builder) {
+        String key = null;
+        while (parser.hasNext()) {
+            final JsonParser.Event next = parser.next();
+            switch (next) {
+                case KEY_NAME:
+                    key = parser.getString();
+                    break;
+
+                case VALUE_STRING:
+                    builder.add(key, new JsonStringImpl(parser.getString()));
+                    break;
+
+                case START_OBJECT:
+                    JsonObjectBuilder subObject = null;
+                    parseObject(subObject = new JsonObjectBuilderImpl());
+                    builder.add(key, subObject);
+                    break;
+
+                case START_ARRAY:
+                    JsonArrayBuilder subArray = null;
+                    parseArray(subArray = new JsonArrayBuilderImpl());
+                    builder.add(key, subArray);
+                    break;
+
+                case VALUE_NUMBER:
+                    if (parser.isIntegralNumber() && parser.isNotTooLong()) {
+                        builder.add(key, new JsonLongImpl(parser.getLong()));
+                    } else {
+                        builder.add(key, new JsonNumberImpl(parser.getBigDecimal()));
+                    }
+                    break;
+
+                case VALUE_NULL:
+                    builder.addNull(key);
+                    break;
+
+                case VALUE_TRUE:
+                    builder.add(key, true);
+                    break;
+
+                case VALUE_FALSE:
+                    builder.add(key, false);
+                    break;
+
+                case END_OBJECT:
+                    return;
+
+                case END_ARRAY:
+                    throw new JsonParsingException("']', shouldn't occur", parser.getLocation());
+
+                default:
+                    throw new JsonParsingException(next.name() + ", shouldn't occur", parser.getLocation());
+            }
+        }
+    }
+
+    private void parseArray(final JsonArrayBuilder builder) {
+        while (parser.hasNext()) {
+            final JsonParser.Event next = parser.next();
+            switch (next) {
+                case VALUE_STRING:
+                    builder.add(new JsonStringImpl(parser.getString()));
+                    break;
+
+                case VALUE_NUMBER:
+                    if (parser.isIntegralNumber()) {
+                        builder.add(new JsonLongImpl(parser.getLong()));
+                    } else {
+                        builder.add(new JsonNumberImpl(parser.getBigDecimal()));
+                    }
+                    break;
+
+                case START_OBJECT:
+                    JsonObjectBuilder subObject = null;
+                    parseObject(subObject = new JsonObjectBuilderImpl());
+                    builder.add(subObject);
+                    break;
+
+                case START_ARRAY:
+                    JsonArrayBuilder subArray = null;
+                    parseArray(subArray = new JsonArrayBuilderImpl());
+                    builder.add(subArray);
+                    break;
+
+                case END_ARRAY:
+                    return;
+
+                case VALUE_NULL:
+                    builder.addNull();
+                    break;
+
+                case VALUE_TRUE:
+                    builder.add(true);
+                    break;
+
+                case VALUE_FALSE:
+                    builder.add(false);
+                    break;
+
+                case KEY_NAME:
+                    throw new JsonParsingException("array doesn't have keys", parser.getLocation());
+
+                case END_OBJECT:
+                    throw new JsonParsingException("'}', shouldn't occur", parser.getLocation());
+
+                default:
+                    throw new JsonParsingException(next.name() + ", shouldn't occur", parser.getLocation());
+            }
+        }
+    }
+
+    private void checkClosed() {
+        if (closed) {
+            throw new IllegalStateException("read(), readObject(), readArray() or close() method was already called");
+        }
+
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonStreamParserImpl.java b/src/main/java/org/apache/johnzon/core/JsonStreamParserImpl.java
new file mode 100644
index 0000000..b8a0005
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonStreamParserImpl.java
@@ -0,0 +1,1071 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import javax.json.JsonArray;
+import javax.json.JsonException;
+import javax.json.JsonObject;
+import javax.json.JsonValue;
+import javax.json.stream.JsonLocation;
+import javax.json.stream.JsonParsingException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.math.BigDecimal;
+import java.nio.charset.Charset;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.stream.Stream;
+
+//This class represents either the Json tokenizer and the Json parser.
+public class JsonStreamParserImpl implements JsonChars, JohnzonJsonParser {
+    private final boolean autoAdjust;
+
+    //the main buffer where the stream will be buffered
+    private final char[] buffer;
+
+    //current parser position within the buffer
+    //Initial MIN_VALUE will trigger buffer refill, normally bufferPos is >= -1
+    //-1 would cause a re-read of the first character in the buffer (which is at zero index)
+    private int bufferPos = Integer.MIN_VALUE;
+
+    //available character in the buffer. It might be <= "buffer.length".
+    private int availableCharsInBuffer;
+
+    //start and end position of values in the buffer
+    //may cross boundaries, then value is in fallBackCopyBuffer
+    private int startOfValueInBuffer = -1;
+    private int endOfValueInBuffer = -1;
+
+    private final Reader in;
+
+    //do we read from a character stream or a byte stream
+    //not used at the moment but maybe relevant in future to calculate the JsonLocation offset
+    @SuppressWarnings("unused")
+    private final BufferStrategy.BufferProvider<char[]> bufferProvider;
+    private final BufferStrategy.BufferProvider<char[]> valueProvider;
+
+    //max length for strings and numbers (max count of characters)
+    private final int maxValueLength;
+
+    //we use a byte here, because comparing bytes
+    //is more efficient than comparing enums
+    //Additionally we handle internally two more event: COMMA_EVENT and KEY_SEPARATOR_EVENT
+    private byte previousEvent;
+
+    //this buffer is used to store current String or Number value in case that
+    //within the value a buffer boundary is crossed or the string contains escaped characters
+    private char[] fallBackCopyBuffer;
+    private boolean releaseFallBackCopyBufferLength = true;
+    private int fallBackCopyBufferLength;
+
+    // location (line, column, offset)
+    // We try to calculate this efficiently so we do not just increment the values per char read
+    // Instead we calculate the column and offset relative to the pastBufferReadCount and/or lastLineBreakPosition.
+    private long currentLine = 1;
+    private long lastLineBreakPosition;
+    private long pastBufferReadCount;
+
+    //cache (if current value is a number) integral state and the number itself if its only one digit    
+    private boolean isCurrentNumberIntegral = true;
+    private int currentIntegralNumber = Integer.MIN_VALUE; //for number from 0 - 9
+
+    //maybe we want also cache BigDecimals
+    //private BigDecimal currentBigDecimalNumber = null;
+
+    //We need a stack if we want detect bad formatted Json do determine if we are within an array or not
+    //example
+    //     Streamparser sees: ],1  <-- we look from here
+    //the 1 is only allowed if we are within an array
+    //This can only be determined by build up a stack which tracks the trail of Json objects and arrays
+    //This stack here is only needed for validating the above mentioned case, if we want to be lenient we can skip suing the stack.
+    //Stack can cause out of memory issues when the nesting depth of a Json stream is too deep.
+    private StructureElement currentStructureElement = null;
+
+    //minimal stack implementation
+    private static final class StructureElement {
+        private final StructureElement previous;
+        private final boolean isArray;
+
+        StructureElement(final StructureElement previous, final boolean isArray) {
+            super();
+            this.previous = previous;
+            this.isArray = isArray;
+        }
+    }
+
+    //detect charset according to RFC 4627
+    public JsonStreamParserImpl(final InputStream inputStream, final int maxStringLength,
+                                final BufferStrategy.BufferProvider<char[]> bufferProvider, final BufferStrategy.BufferProvider<char[]> valueBuffer,
+                                final boolean autoAdjust) {
+
+        this(inputStream, null, null, maxStringLength, bufferProvider, valueBuffer, autoAdjust);
+    }
+
+    //use charset provided
+    public JsonStreamParserImpl(final InputStream inputStream, final Charset encoding, final int maxStringLength,
+                                final BufferStrategy.BufferProvider<char[]> bufferProvider, final BufferStrategy.BufferProvider<char[]> valueBuffer,
+                                final boolean autoAdjust) {
+
+        this(inputStream, null, encoding, maxStringLength, bufferProvider, valueBuffer, autoAdjust);
+    }
+
+    public JsonStreamParserImpl(final Reader reader, final int maxStringLength, final BufferStrategy.BufferProvider<char[]> bufferProvider,
+                                final BufferStrategy.BufferProvider<char[]> valueBuffer, final boolean autoAdjust) {
+
+        this(null, reader, null, maxStringLength, bufferProvider, valueBuffer, autoAdjust);
+    }
+
+    private JsonStreamParserImpl(final InputStream inputStream, final Reader reader, final Charset encoding, final int maxStringLength,
+                                 final BufferStrategy.BufferProvider<char[]> bufferProvider, final BufferStrategy.BufferProvider<char[]> valueBuffer,
+                                 final boolean autoAdjust) {
+
+        this.autoAdjust = autoAdjust;
+        this.maxValueLength = maxStringLength <= 0 ? 8192 : maxStringLength;
+        this.fallBackCopyBuffer = valueBuffer.newBuffer();
+        this.buffer = bufferProvider.newBuffer();
+        this.bufferProvider = bufferProvider;
+        this.valueProvider = valueBuffer;
+
+        if (fallBackCopyBuffer.length < maxStringLength) {
+            throw cust("Size of value buffer cannot be smaller than maximum string length");
+        }
+
+        if (reader != null) {
+            this.in = reader;
+        } else if (encoding == null) {
+            this.in = new RFC4627AwareInputStreamReader(inputStream);
+
+        } else {
+            this.in = new InputStreamReader(inputStream, encoding.newDecoder());
+        }
+    }
+
+    //append a single char to the value buffer
+    private void appendToCopyBuffer(final char c) {
+        fallBackCopyBuffer[fallBackCopyBufferLength++] = c;
+    }
+
+    //copy content between "start" and "end" from buffer to value buffer 
+    private void copyCurrentValue() {
+        final int length = endOfValueInBuffer - startOfValueInBuffer;
+        if (length > 0) {
+
+            if (length > maxValueLength) {
+                throw tmc();
+            }
+
+            if (fallBackCopyBufferLength >= fallBackCopyBuffer.length - length) { // not good at runtime but handled
+                if (!autoAdjust) {
+                    throw new ArrayIndexOutOfBoundsException("Buffer too small for such a long string");
+                }
+
+                final char[] newArray = new char[fallBackCopyBuffer.length + getBufferExtends(fallBackCopyBuffer.length)];
+                // TODO: log to adjust size once?
+                System.arraycopy(fallBackCopyBuffer, 0, newArray, 0, fallBackCopyBufferLength);
+                System.arraycopy(buffer, startOfValueInBuffer, newArray, fallBackCopyBufferLength, length);
+                if (releaseFallBackCopyBufferLength) {
+                    bufferProvider.release(fallBackCopyBuffer);
+                    releaseFallBackCopyBufferLength = false;
+                }
+                fallBackCopyBuffer = newArray;
+            } else {
+                System.arraycopy(buffer, startOfValueInBuffer, fallBackCopyBuffer, fallBackCopyBufferLength, length);
+            }
+            fallBackCopyBufferLength += length;
+        }
+
+        startOfValueInBuffer = endOfValueInBuffer = -1;
+    }
+
+    /**
+     * @return the amount of bytes the current buffer should get extended with
+     */
+    protected int getBufferExtends(int currentLength) {
+        return currentLength/4;
+    }
+
+
+    @Override
+    public final boolean hasNext() {
+
+        if (currentStructureElement != null ||
+                (previousEvent != END_ARRAY && previousEvent != END_OBJECT &&
+                        previousEvent != VALUE_STRING && previousEvent != VALUE_FALSE && previousEvent != VALUE_TRUE &&
+                        previousEvent != VALUE_NULL && previousEvent != VALUE_NUMBER) ||
+                previousEvent == 0) {
+
+            return true;
+        }
+
+        //detect garbage at the end of the file after last object or array is closed
+        if (bufferPos < availableCharsInBuffer) {
+
+            final char c = readNextNonWhitespaceChar(readNextChar());
+
+            if (c == EOF) {
+                return false;
+            }
+
+            if (bufferPos < availableCharsInBuffer) {
+                throw uexc("EOF expected");
+            }
+
+        }
+
+        return false;
+
+    }
+
+    private static boolean isAsciiDigit(final char value) {
+        return value <= NINE && value >= ZERO;
+    }
+
+    //check if value is a valid hex digit and return the numeric value
+    private int parseHexDigit(final char value) {
+
+        if (isAsciiDigit(value)) {
+            return value - 48;
+        } else if (value <= 'f' && value >= 'a') {
+            return (value) - 87;
+        } else if ((value <= 'F' && value >= 'A')) {
+            return (value) - 55;
+        } else {
+            throw uexc("Invalid hex character");
+        }
+    }
+
+    private JsonLocation createLocation() {
+
+        //we start with column = 1, so column is always >= 1
+        //APi is not clear in this, but starting column with 1 is convenient
+        long column = 1;
+        long charOffset = 0;
+
+        if (bufferPos >= -1) {
+
+            charOffset = pastBufferReadCount + bufferPos + 1;
+            column = lastLineBreakPosition == 0 ? charOffset + 1 : charOffset - lastLineBreakPosition;
+        }
+
+        //For now its unclear how to calculate offset for (byte) inputsream.
+        //API says count bytes but thats dependent on encoding and not efficient
+        //skip this for now, count always bytes and defer this until the JSR TCK arrives.
+
+        return new JsonLocationImpl(currentLine, column, charOffset);
+    }
+
+    //read the next char from the stream and set/increment the bufferPos
+    //will also refill buffer if necessary
+    //if we are currently processing a value (string or number) and buffer 
+    //refill is necessary copy the already read value part into the value buffer
+    protected final char readNextChar() {
+
+        if ((availableCharsInBuffer - bufferPos) <= 1) {
+            //fillbuffer
+
+            //copy content from old buffer to valuebuffer
+            //correct start end mark
+            if (startOfValueInBuffer > -1 && endOfValueInBuffer == -1) {
+                endOfValueInBuffer = availableCharsInBuffer;
+                copyCurrentValue();
+
+                startOfValueInBuffer = 0;
+            }
+
+            if (bufferPos >= -1) {
+                pastBufferReadCount += availableCharsInBuffer;
+            }
+
+            try {
+                availableCharsInBuffer = in.read(buffer, 0, buffer.length);
+                if (availableCharsInBuffer <= 0) {
+                    return EOF;
+                }
+
+            } catch (final IOException e) {
+                close();
+                throw uexio(e);
+            }
+
+            bufferPos = 0;
+            //end fillbuffer
+        } else {
+
+            //since JOHNZON-18 not longer necessary
+            //prevent "bufferoverflow
+            //if(bufferPos + 1 >= availableCharsInBuffer) {
+            //    return EOF;
+            //}
+
+            bufferPos++;
+        }
+
+        return buffer[bufferPos];
+    }
+
+    //skip whitespaces
+    //tracks location informations (line, column)
+    //returns the first non whitespace character
+    protected final char readNextNonWhitespaceChar(char c) {
+
+        int dosCount = 0;
+
+        while (c == SPACE || c == TAB || c == CR || c == EOL) {
+
+            if (c == EOL) {
+                currentLine++;
+                lastLineBreakPosition = pastBufferReadCount + bufferPos;
+            }
+
+            //prevent DOS (denial of service) attack
+            if (dosCount >= maxValueLength) {
+                throw tmc();
+            }
+            dosCount++;
+
+            //read next character
+            c = readNextChar();
+
+        }
+
+        return c;
+    }
+
+    @Override
+    public final Event next() {
+        //main entry, make decision how to handle the current character in the stream
+
+        if (!hasNext()) {
+            throw new NoSuchElementException();
+        }
+
+        if (previousEvent != 0 && currentStructureElement == null) {
+            throw uexc("Unexpected end of structure");
+        }
+
+        final char c = readNextNonWhitespaceChar(readNextChar());
+
+        if (c == COMMA_CHAR) {
+            //last event must one of the following-> " ] } LITERAL
+            if (previousEvent == KEY_SEPARATOR_EVENT || previousEvent == START_ARRAY
+                    || previousEvent == START_OBJECT || previousEvent == COMMA_EVENT
+                    || previousEvent == KEY_NAME) {
+                throw uexc("Expected \" ] } LITERAL");
+            }
+
+            previousEvent = COMMA_EVENT;
+            return next();
+
+        }
+
+        if (c == KEY_SEPARATOR) {
+
+            if (previousEvent != KEY_NAME) {
+                throw uexc("A : can only follow a key name");
+            }
+
+            previousEvent = KEY_SEPARATOR_EVENT;
+            return next();
+
+        }
+
+        if (!isCurrentNumberIntegral) {
+            isCurrentNumberIntegral = true;
+        }
+        //        if (currentBigDecimalNumber != null) {
+        //            currentBigDecimalNumber = null;
+        //        }
+        if (currentIntegralNumber != Integer.MIN_VALUE) {
+            currentIntegralNumber = Integer.MIN_VALUE;
+        }
+
+        if (fallBackCopyBufferLength != 0) {
+            fallBackCopyBufferLength = 0;
+        }
+
+        startOfValueInBuffer = endOfValueInBuffer = -1;
+
+        switch (c) {
+
+            case START_OBJECT_CHAR:
+
+                return handleStartObject();
+
+            case END_OBJECT_CHAR:
+
+                return handleEndObject();
+
+            case START_ARRAY_CHAR:
+
+                return handleStartArray();
+
+            case END_ARRAY_CHAR:
+
+                return handleEndArray();
+
+            case QUOTE_CHAR:
+
+                return handleQuote();
+
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+            case MINUS:
+            case FALSE_F: // false
+            case TRUE_T: // true
+            case NULL_N: // null
+
+                return handleLiteral();
+
+            default:
+
+                return defaultHandling(c);
+        }
+    }
+
+    protected Event defaultHandling(char c) {
+        if (c == EOF) {
+            throw uexc("End of file hit too early");
+        }
+        throw uexc("Expected structural character or digit or 't' or 'n' or 'f' or '-'");
+    }
+
+    private Event handleStartObject() {
+
+        //last event must one of the following-> : , [
+        if (previousEvent != 0 && previousEvent != KEY_SEPARATOR_EVENT && previousEvent != START_ARRAY && previousEvent != COMMA_EVENT) {
+            throw uexc("Expected : , [");
+        }
+
+        //push upon the stack
+        if (currentStructureElement == null) {
+            currentStructureElement = new StructureElement(null, false);
+        } else {
+            if (!currentStructureElement.isArray && previousEvent != KEY_SEPARATOR_EVENT) {
+                throw uexc("Expected :");
+            }
+            final StructureElement localStructureElement = new StructureElement(currentStructureElement, false);
+            currentStructureElement = localStructureElement;
+        }
+
+        return EVT_MAP[previousEvent = START_OBJECT];
+
+    }
+
+    private Event handleEndObject() {
+
+        //last event must one of the following-> " ] { } LITERAL
+        if (previousEvent == START_ARRAY || previousEvent == COMMA_EVENT || previousEvent == KEY_NAME
+                || previousEvent == KEY_SEPARATOR_EVENT || currentStructureElement == null) {
+            throw uexc("Expected \" ] { } LITERAL");
+        }
+
+        if (currentStructureElement.isArray) {
+            throw uexc("Expected : ]");
+        }
+
+        //pop from stack
+        currentStructureElement = currentStructureElement.previous;
+
+        return EVT_MAP[previousEvent = END_OBJECT];
+    }
+
+    private Event handleStartArray() {
+
+        //last event must one of the following-> : , [
+        if (previousEvent != 0 && previousEvent != KEY_SEPARATOR_EVENT && previousEvent != START_ARRAY && previousEvent != COMMA_EVENT) {
+            throw uexc("Expected : , [");
+        }
+
+        //push upon the stack
+        if (currentStructureElement == null) {
+            currentStructureElement = new StructureElement(null, true);
+        } else {
+            if (!currentStructureElement.isArray && previousEvent != KEY_SEPARATOR_EVENT) {
+                throw uexc("Expected \"");
+            }
+            final StructureElement localStructureElement = new StructureElement(currentStructureElement, true);
+            currentStructureElement = localStructureElement;
+        }
+
+        return EVT_MAP[previousEvent = START_ARRAY];
+    }
+
+    private Event handleEndArray() {
+
+        //last event must one of the following-> [ ] } " LITERAL
+        if (previousEvent == START_OBJECT || previousEvent == COMMA_EVENT || previousEvent == KEY_SEPARATOR_EVENT
+                || currentStructureElement == null) {
+            throw uexc("Expected [ ] } \" LITERAL");
+        }
+
+        if (!currentStructureElement.isArray) {
+            throw uexc("Expected : }");
+        }
+
+        //pop from stack
+        currentStructureElement = currentStructureElement.previous;
+
+        return EVT_MAP[previousEvent = END_ARRAY];
+    }
+
+    //read a string, gets called recursively
+    //Handles escape/d characters
+    //if string contains escape chars and/or cross buffer boundary then copy in the value buffer
+    //if not then denote string start and end in startOfValueInBuffer and endOfValueInBuffer and read directly from buffer
+    private void readString() {
+
+        do {
+            char n = readNextChar();
+            //when first called n its first char after the starting quote
+            //after that its the next character after the while loop below
+
+            if (n == QUOTE_CHAR) {
+                endOfValueInBuffer = startOfValueInBuffer = bufferPos; //->"" case
+                return;
+            } else if (n == EOL) {
+                throw uexc("Unexpected linebreak");
+
+            } else if (/* n >= '\u0000' && */ n <= '\u001F') {
+                throw uexc("Unescaped control character");
+
+            } else if (n == ESCAPE_CHAR) {
+
+                n = readNextChar();
+
+                //  \ u XXXX -> unicode char
+                if (n == 'u') {
+                    n = parseUnicodeHexChars();
+                    appendToCopyBuffer(n);
+
+                    // \\ -> \
+                } else if (n == ESCAPE_CHAR) {
+                    appendToCopyBuffer(n);
+
+                    //another escape chars, for example \t
+                } else {
+                    appendToCopyBuffer(Strings.asEscapedChar(n));
+
+                }
+
+            } else {
+
+                startOfValueInBuffer = bufferPos;
+                endOfValueInBuffer = -1;
+
+                while ((n = readNextChar()) > '\u001F' && n != ESCAPE_CHAR && n != EOL && n != QUOTE_CHAR) {
+                    //read fast
+                }
+
+                endOfValueInBuffer = bufferPos;
+
+                if (n == QUOTE_CHAR) {
+
+                    if (fallBackCopyBufferLength > 0) {
+                        copyCurrentValue();
+                    } else {
+                        if ((endOfValueInBuffer - startOfValueInBuffer) > maxValueLength) {
+                            throw tmc();
+                        }
+
+                    }
+
+                    return;
+                } else if (n == EOL) {
+                    throw uexc("Unexpected linebreak");
+
+                } else if (n >= '\u0000' && n <= '\u001F') {
+                    throw uexc("Unescaped control character");
+                }
+
+                copyCurrentValue();
+
+                //current n is one of < '\u001F' -OR- ESCAPE_CHAR -OR- EOL -OR- QUOTE
+
+                bufferPos--; //unread one char
+
+            }
+        } while (true);
+
+        // before this do while(true) it was:
+        //
+        //recurse until string is terminated by a non escaped quote
+        //readString();
+        //
+        //
+        // but recursive = can't read big strings
+
+    }
+
+    //maybe we want to check invalid utf encoding
+    //not clear yet if the InputStreamReader is doing that
+
+    /*
+    private char checkSurrogates(char n, char highSurrogate) {
+        //check for invalid surrogates
+        //high followed by low       
+        if (Character.isHighSurrogate(n)) {
+
+            if (highSurrogate != 0) {
+                throw uexc("Unexpected high surrogate");
+            }
+            return n;
+        } else if (Character.isLowSurrogate(n)) {
+
+            if (highSurrogate == 0) {
+                throw uexc("Unexpected low surrogate");
+            } else if (!Character.isSurrogatePair(highSurrogate, n)) {
+                throw uexc("Invalid surrogate pair");
+            }
+            return 0;
+        } else if (highSurrogate != 0 && !Character.isLowSurrogate(n)) {
+            throw uexc("Expected low surrogate");
+        }
+        
+        return highSurrogate;
+    }*/
+
+    //read the next four chars, check them and treat them as an single unicode char
+    private char parseUnicodeHexChars() {
+        // \u08Ac etc       
+        return (char) (((parseHexDigit(readNextChar())) * 4096) + ((parseHexDigit(readNextChar())) * 256)
+                + ((parseHexDigit(readNextChar())) * 16) + ((parseHexDigit(readNextChar()))));
+
+    }
+
+    private Event handleQuote() {
+
+        //always the beginning quote of a key or value  
+
+        //last event must one of the following-> : { [ ,
+        if (previousEvent != KEY_SEPARATOR_EVENT && previousEvent != START_OBJECT && previousEvent != START_ARRAY
+                && previousEvent != COMMA_EVENT) {
+            throw uexc("Expected : { [ ,");
+        }
+        //starting quote already consumed
+        readString();
+        //end quote already consumed
+
+        //make the decision if its an key or value
+        if (previousEvent == KEY_SEPARATOR_EVENT) {
+            //must be value
+
+            if (currentStructureElement != null && currentStructureElement.isArray) {
+                //not in array, only allowed within array
+                throw uexc("Key value pair not allowed in an array");
+            }
+
+            return EVT_MAP[previousEvent = VALUE_STRING];
+
+        } else { //Event is  START_OBJECT  OR START_ARRAY OR COMMA_EVENT
+            //must be a key if we are in an object, if not its a value 
+
+            if ((currentStructureElement != null && currentStructureElement.isArray) || currentStructureElement == null) {
+                return EVT_MAP[previousEvent = VALUE_STRING];
+            }
+
+            return EVT_MAP[previousEvent = KEY_NAME];
+        }
+
+    }
+
+    //read a number
+    //if a number cross buffer boundary then copy in the value buffer
+    //if not then denote string start and end in startOfValueInBuffer and endOfValueInBuffer and read directly from buffer
+    private void readNumber() {
+
+        char c = buffer[bufferPos];
+
+        //start can change on any read() if we cross buffer boundary
+        startOfValueInBuffer = bufferPos;
+        endOfValueInBuffer = -1;
+
+        char y = EOF;
+
+        //sum up the digit values 
+        int cumulatedDigitValue = 0;
+        while (isAsciiDigit(y = readNextChar())) {
+
+            if (c == ZERO) {
+                throw uexc("Leading zeros not allowed");
+            }
+
+            if (c == MINUS && cumulatedDigitValue == 48) {
+                throw uexc("Leading zeros after minus not allowed");
+            }
+
+            cumulatedDigitValue += y;
+
+        }
+
+        if (c == MINUS && cumulatedDigitValue == 0) {
+
+            throw uexc("Unexpected premature end of number");
+        }
+
+        if (y == DOT) {
+            isCurrentNumberIntegral = false;
+            cumulatedDigitValue = 0;
+            while (isAsciiDigit(y = readNextChar())) {
+                cumulatedDigitValue++;
+            }
+
+            if (cumulatedDigitValue == 0) {
+
+                throw uexc("Unexpected premature end of number");
+            }
+
+        }
+
+        if (y == EXP_LOWERCASE || y == EXP_UPPERCASE) {
+            isCurrentNumberIntegral = false;
+
+            y = readNextChar(); //+ or - or digit
+
+            if (!isAsciiDigit(y) && y != MINUS && y != PLUS) {
+                throw uexc("Expected DIGIT or + or -");
+            }
+
+            if (y == MINUS || y == PLUS) {
+                y = readNextChar();
+                if (!isAsciiDigit(y)) {
+                    throw uexc("Unexpected premature end of number");
+                }
+
+            }
+
+            while (isAsciiDigit(y = readNextChar())) {
+                //no-op
+            }
+
+        }
+
+        endOfValueInBuffer = y == EOF && endOfValueInBuffer < 0 ? -1 : bufferPos;
+
+        if (y == COMMA_CHAR || y == END_ARRAY_CHAR || y == END_OBJECT_CHAR || y == EOL || y == SPACE || y == TAB || y == CR || y == EOF) {
+
+            bufferPos--;//unread one char
+
+            //['-', DIGIT]
+            if (isCurrentNumberIntegral && c == MINUS && cumulatedDigitValue >= 48 && cumulatedDigitValue <= 57) {
+
+                currentIntegralNumber = -(cumulatedDigitValue - 48); //optimize -0 till -9
+                return;
+            }
+
+            //[DIGIT]
+            if (isCurrentNumberIntegral && c != MINUS && cumulatedDigitValue == 0) {
+
+                currentIntegralNumber = (c - 48); //optimize 0 till 9
+                return;
+            }
+
+            if (fallBackCopyBufferLength > 0) {
+
+                //we crossed a buffer boundary, use value buffer
+                copyCurrentValue();
+
+            } else {
+                if ((endOfValueInBuffer - startOfValueInBuffer) >= maxValueLength) {
+                    throw tmc();
+                }
+            }
+
+            return;
+
+        }
+
+        throw uexc("Unexpected premature end of number");
+
+    }
+
+    //handles false, true, null and numbers
+    private Event handleLiteral() {
+
+        //last event must one of the following-> : , [
+        if (previousEvent != KEY_SEPARATOR_EVENT && previousEvent != START_ARRAY && previousEvent != COMMA_EVENT) {
+            throw uexc("Expected : , [");
+        }
+
+        if (previousEvent == COMMA_EVENT && !currentStructureElement.isArray) {
+            //only allowed within array
+            throw uexc("Not in an array context");
+        }
+
+        char c = buffer[bufferPos];
+
+        // probe literals
+        switch (c) {
+            case TRUE_T:
+
+                if (readNextChar() != TRUE_R || readNextChar() != TRUE_U || readNextChar() != TRUE_E) {
+                    throw uexc("Expected LITERAL: true");
+                }
+                return EVT_MAP[previousEvent = VALUE_TRUE];
+            case FALSE_F:
+
+                if (readNextChar() != FALSE_A || readNextChar() != FALSE_L || readNextChar() != FALSE_S || readNextChar() != FALSE_E) {
+                    throw uexc("Expected LITERAL: false");
+                }
+
+                return EVT_MAP[previousEvent = VALUE_FALSE];
+
+            case NULL_N:
+
+                if (readNextChar() != NULL_U || readNextChar() != NULL_L || readNextChar() != NULL_L) {
+                    throw uexc("Expected LITERAL: null");
+                }
+                return EVT_MAP[previousEvent = VALUE_NULL];
+
+            default:
+                readNumber();
+                return EVT_MAP[previousEvent = VALUE_NUMBER];
+        }
+
+    }
+
+    @Override
+    public String getString() {
+        if (previousEvent == KEY_NAME || previousEvent == VALUE_STRING || previousEvent == VALUE_NUMBER) {
+
+            //if there a content in the value buffer read from them, if not use main buffer
+            return fallBackCopyBufferLength > 0 ? new String(fallBackCopyBuffer, 0, fallBackCopyBufferLength) : new String(buffer,
+                    startOfValueInBuffer, endOfValueInBuffer - startOfValueInBuffer);
+        } else {
+            throw new IllegalStateException(EVT_MAP[previousEvent] + " doesn't support getString()");
+        }
+    }
+
+    @Override
+    public boolean isIntegralNumber() {
+
+        if (previousEvent != VALUE_NUMBER) {
+            throw new IllegalStateException(EVT_MAP[previousEvent] + " doesn't support isIntegralNumber()");
+        } else {
+            return isCurrentNumberIntegral;
+        }
+    }
+
+    @Override
+    public boolean isNotTooLong() {
+        return (endOfValueInBuffer - startOfValueInBuffer) < 19;
+    }
+
+    @Override
+    public int getInt() {
+        if (previousEvent != VALUE_NUMBER) {
+            throw new IllegalStateException(EVT_MAP[previousEvent] + " doesn't support getInt()");
+        } else if (isCurrentNumberIntegral && currentIntegralNumber != Integer.MIN_VALUE) {
+            return currentIntegralNumber;
+        } else if (isCurrentNumberIntegral) {
+            //if there a content in the value buffer read from them, if not use main buffer
+            final Integer retVal = fallBackCopyBufferLength > 0 ? parseIntegerFromChars(fallBackCopyBuffer, 0, fallBackCopyBufferLength)
+                    : parseIntegerFromChars(buffer, startOfValueInBuffer, endOfValueInBuffer);
+            if (retVal == null) {
+                return getBigDecimal().intValue();
+            } else {
+                return retVal.intValue();
+            }
+        } else {
+            return getBigDecimal().intValue();
+        }
+    }
+
+    @Override
+    public long getLong() {
+        if (previousEvent != VALUE_NUMBER) {
+            throw new IllegalStateException(EVT_MAP[previousEvent] + " doesn't support getLong()");
+        } else if (isCurrentNumberIntegral && currentIntegralNumber != Integer.MIN_VALUE) {
+            return currentIntegralNumber;
+        } else if (isCurrentNumberIntegral) {
+            //if there a content in the value buffer read from them, if not use main buffer
+            final Long retVal = fallBackCopyBufferLength > 0 ? parseLongFromChars(fallBackCopyBuffer, 0, fallBackCopyBufferLength)
+                    : parseLongFromChars(buffer, startOfValueInBuffer, endOfValueInBuffer);
+            if (retVal == null) {
+                return getBigDecimal().longValue();
+            } else {
+                return retVal.longValue();
+            }
+        } else {
+            return getBigDecimal().longValue();
+        }
+
+    }
+
+    @Override
+    public BigDecimal getBigDecimal() {
+        if (previousEvent != VALUE_NUMBER) {
+            throw new IllegalStateException(EVT_MAP[previousEvent] + " doesn't support getBigDecimal()");
+            //        } else if (currentBigDecimalNumber != null) {
+            //            return currentBigDecimalNumber;
+        } else if (isCurrentNumberIntegral && currentIntegralNumber != Integer.MIN_VALUE) {
+            return new BigDecimal(currentIntegralNumber);
+        } else if (isCurrentNumberIntegral) {
+            //if there a content in the value buffer read from them, if not use main buffer
+            final Long retVal = fallBackCopyBufferLength > 0 ? parseLongFromChars(fallBackCopyBuffer, 0, fallBackCopyBufferLength)
+                    : parseLongFromChars(buffer, startOfValueInBuffer, endOfValueInBuffer);
+            if (retVal == null) {
+                return (/*currentBigDecimalNumber = */fallBackCopyBufferLength > 0 ? new BigDecimal(fallBackCopyBuffer, 0,
+                        fallBackCopyBufferLength) : new BigDecimal(buffer, startOfValueInBuffer,
+                        (endOfValueInBuffer - startOfValueInBuffer)));
+            } else {
+                return (/*currentBigDecimalNumber = */new BigDecimal(retVal.longValue()));
+            }
+        } else {
+            //if there a content in the value buffer read from them, if not use main buffer
+            return (/*currentBigDecimalNumber = */fallBackCopyBufferLength > 0 ? new BigDecimal(fallBackCopyBuffer, 0,
+                    fallBackCopyBufferLength) : new BigDecimal(buffer, startOfValueInBuffer, (endOfValueInBuffer - startOfValueInBuffer)));
+        }
+
+    }
+
+    @Override
+    public JsonLocation getLocation() {
+        return createLocation();
+    }
+
+    @Override
+    public void close() {
+        bufferProvider.release(buffer);
+        if (releaseFallBackCopyBufferLength) {
+            valueProvider.release(fallBackCopyBuffer);
+        }
+
+        try {
+            in.close();
+        } catch (final IOException e) {
+            throw new JsonException("Unexpected IO exception " + e.getMessage(), e);
+        }
+    }
+
+    //parse a char[] to long while checking overflow
+    //if overflowed return null
+    //no additional checks since we are sure here that there are no non digits in the array
+    private static Long parseLongFromChars(final char[] chars, final int start, final int end) {
+
+        long retVal = 0;
+        final boolean negative = chars[start] == MINUS;
+        for (int i = negative ? start + 1 : start; i < end; i++) {
+            final long tmp = retVal * 10 + (chars[i] - ZERO);
+            if (tmp < retVal) { //check overflow
+                return null;
+            } else {
+                retVal = tmp;
+            }
+        }
+
+        return negative ? -retVal : retVal;
+    }
+
+    //parse a char[] to int while checking overflow
+    //if overflowed return null
+    //no additional checks since we are sure here that there are no non digits in the array
+    private static Integer parseIntegerFromChars(final char[] chars, final int start, final int end) {
+
+        int retVal = 0;
+        final boolean negative = chars[start] == MINUS;
+        for (int i = negative ? start + 1 : start; i < end; i++) {
+            final int tmp = retVal * 10 + (chars[i] - ZERO);
+            if (tmp < retVal) { //check overflow
+                return null;
+            } else {
+                retVal = tmp;
+            }
+        }
+
+        return negative ? -retVal : retVal;
+    }
+
+    @Override
+    public JsonObject getObject() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public JsonValue getValue() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public JsonArray getArray() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Stream<JsonValue> getArrayStream() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Stream<Map.Entry<String, JsonValue>> getObjectStream() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Stream<JsonValue> getValueStream() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void skipArray() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void skipObject() {
+        throw new UnsupportedOperationException();
+    }
+
+    private JsonParsingException uexc(final char c, final String message) {
+        final JsonLocation location = createLocation();
+        return new JsonParsingException("Unexpected character '" + c + "' (Codepoint: " + String.valueOf(c).codePointAt(0) + ") on "
+                + location + ". Reason is [[" + message + "]]", location);
+    }
+
+    private JsonParsingException uexc(final String message) {
+        final char c = bufferPos < 0 ? 0 : buffer[bufferPos];
+        return uexc(c, message);
+    }
+
+    private JsonParsingException tmc() {
+        final JsonLocation location = createLocation();
+        return new JsonParsingException("Too many characters. Maximum string/number length of " + maxValueLength + " exceeded on "
+                + location + ". Maybe increase org.apache.johnzon.max-string-length in jsonp factory properties or system properties.", location);
+    }
+
+    private JsonParsingException uexio(final IOException e) {
+        final JsonLocation location = createLocation();
+        return new JsonParsingException("Unexpected IO exception on " + location, e, location);
+    }
+
+    private JsonParsingException cust(final String message) {
+        final JsonLocation location = createLocation();
+        return new JsonParsingException("General exception on " + location + ". Reason is [[" + message + "]]", location);
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/johnzon/core/JsonStringImpl.java b/src/main/java/org/apache/johnzon/core/JsonStringImpl.java
new file mode 100644
index 0000000..43b62fa
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonStringImpl.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import javax.json.JsonString;
+import java.io.Serializable;
+
+final class JsonStringImpl implements JsonString, Serializable {
+    private final String value;
+    private String escape;
+    private transient Integer hashCode = null;
+
+
+    JsonStringImpl(final String value) {
+        if(value == null) {
+            throw new NullPointerException("value must not be null");
+        }
+
+        this.value = value;
+    }
+
+    @Override
+    public String getString() {
+        return value;
+    }
+
+    @Override
+    public CharSequence getChars() {
+        return value;
+    }
+
+    @Override
+    public ValueType getValueType() {
+        return ValueType.STRING;
+    }
+
+    @Override
+    public String toString() {
+        String s = escape;
+        if (s == null) {
+            s =  JsonChars.QUOTE_CHAR+Strings.escape(value)+JsonChars.QUOTE_CHAR;
+            escape=s;
+        }
+        return s;
+    }
+
+    @Override
+    public int hashCode() {
+        Integer h = hashCode;
+        if (h == null) {
+            h = value.hashCode();
+            hashCode=h;
+        }
+        return h;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return JsonString.class.isInstance(obj) && JsonString.class.cast(obj).getString().equals(value);
+    }
+
+    @Override
+    public JsonObject asJsonObject() {
+        return JsonObject.class.cast(this);
+    }
+
+    @Override
+    public JsonArray asJsonArray() {
+        return JsonArray.class.cast(this);
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonWriterFactoryImpl.java b/src/main/java/org/apache/johnzon/core/JsonWriterFactoryImpl.java
new file mode 100644
index 0000000..84f3df2
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonWriterFactoryImpl.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import static java.util.Arrays.asList;
+
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+import javax.json.JsonWriter;
+import javax.json.JsonWriterFactory;
+import javax.json.stream.JsonGeneratorFactory;
+
+class JsonWriterFactoryImpl extends AbstractJsonFactory implements JsonWriterFactory{
+    private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
+    static final Collection<String> SUPPORTED_CONFIG_KEYS = asList(
+
+    );
+    private final JsonGeneratorFactory factory;
+
+    JsonWriterFactoryImpl(final Map<String, ?> config) {
+        super(config, SUPPORTED_CONFIG_KEYS, JsonGeneratorFactoryImpl.SUPPORTED_CONFIG_KEYS);
+        this.factory = new JsonGeneratorFactoryImpl(internalConfig);
+    }
+
+    @Override
+    public JsonWriter createWriter(final Writer writer) {
+        return new JsonWriterImpl(factory.createGenerator(writer));
+    }
+
+    @Override
+    public JsonWriter createWriter(final OutputStream out) {
+        return createWriter(new OutputStreamWriter(out, UTF8_CHARSET));
+    }
+
+    @Override
+    public JsonWriter createWriter(final OutputStream out, final Charset charset) {
+        return createWriter(new OutputStreamWriter(out, charset));
+    }
+
+    @Override
+    public Map<String, ?> getConfigInUse() {
+        return Collections.unmodifiableMap(internalConfig);
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/JsonWriterImpl.java b/src/main/java/org/apache/johnzon/core/JsonWriterImpl.java
new file mode 100644
index 0000000..2577f1f
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/JsonWriterImpl.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import javax.json.JsonStructure;
+import javax.json.JsonValue;
+import javax.json.JsonWriter;
+import javax.json.stream.JsonGenerator;
+import java.io.Serializable;
+
+class JsonWriterImpl implements JsonWriter, Serializable {
+    private final JsonGenerator generator;
+    private boolean closed = false;
+
+    JsonWriterImpl(final JsonGenerator generator) {
+        this.generator = generator;
+    }
+
+    @Override
+    public void writeArray(final JsonArray array) {
+        checkClosed();
+        try {
+            generator.write(array);
+        } finally {
+            close();
+        }
+    }
+
+    @Override
+    public void writeObject(final JsonObject object) {
+        checkClosed();
+        try {
+            generator.write(object);
+        } finally {
+            close();
+        }
+    }
+
+    @Override
+    public void write(final JsonValue value) {
+        checkClosed();
+        try {
+            generator.write(value);
+        } finally {
+            close();
+        }
+    }
+
+    @Override
+    public void write(final JsonStructure value) {
+        checkClosed();
+        try {
+            generator.write(value);
+        } finally {
+            close();
+        }
+    }
+
+    @Override
+    public void close() {
+
+        if (!closed) {
+            closed = true;
+            generator.close();
+        }
+    }
+
+    private void checkClosed() {
+        if (closed) {
+            throw new IllegalStateException("writeArray(), writeObject(), write() or close() method was already called");
+        }
+
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/RFC4627AwareInputStreamReader.java b/src/main/java/org/apache/johnzon/core/RFC4627AwareInputStreamReader.java
new file mode 100644
index 0000000..645cf0e
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/RFC4627AwareInputStreamReader.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PushbackInputStream;
+import java.nio.charset.Charset;
+
+import javax.json.JsonException;
+
+final class RFC4627AwareInputStreamReader extends InputStreamReader {
+
+    RFC4627AwareInputStreamReader(final InputStream in) {
+        this(new PushbackInputStream(in,4));
+    }
+    
+    private RFC4627AwareInputStreamReader(final PushbackInputStream in) {
+        super(in, getCharset(in).newDecoder());
+       
+    }
+
+    /**
+     * According to the Java API "An attempt is made to read as many as len bytes, but a smaller number may be read".
+     * [http://docs.oracle.com/javase/7/docs/api/java/io/InputStream.html#read(byte[],%20int,%20int)]
+     * For this reason we need to ensure that we've read all the bytes that we need out of this stream.
+     */
+    private static byte[] readAllBytes(final PushbackInputStream inputStream) throws IOException {
+        final int first = inputStream.read();
+        final int second = inputStream.read();
+        if(first == -1|| second == -1) {
+            throw new JsonException("Invalid Json. Valid Json has at least 2 bytes");
+        }
+        final int third = inputStream.read();
+        final int fourth = inputStream.read();
+        if(third == -1) {
+            return new byte[] { (byte) first, (byte) second };
+        } else if(fourth == -1) {
+            return new byte[] { (byte) first, (byte) second, (byte) third };
+        } else {
+            return new byte[] { (byte) first, (byte) second, (byte) third, (byte) fourth };
+        }
+    }
+
+    /*
+        * RFC 4627
+
+          JSON text SHALL be encoded in Unicode.  The default encoding is
+          UTF-8.
+       
+          Since the first two characters of a JSON text will always be ASCII
+          characters [RFC0020], it is possible to determine whether an octet
+          stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking
+          at the pattern of nulls in the first four octets.
+
+          00 00 00 xx  UTF-32BE
+          00 xx 00 xx  UTF-16BE
+          xx 00 00 00  UTF-32LE
+          xx 00 xx 00  UTF-16LE
+          xx xx xx xx  UTF-8
+
+        */
+
+    private static Charset getCharset(final PushbackInputStream inputStream) {
+        Charset charset = Charset.forName("UTF-8");
+        int bomLength=0;
+        try {
+            final byte[] utfBytes = readAllBytes(inputStream);
+            int first = (utfBytes[0] & 0xFF);
+            int second = (utfBytes[1] & 0xFF);
+            if (first == 0x00) {
+                charset = (second == 0x00) ? Charset.forName("UTF-32BE") : Charset.forName("UTF-16BE");
+            } else if (utfBytes.length > 2 && second == 0x00) {
+                int third = (utfBytes[2] & 0xFF);
+                charset = (third  == 0x00) ? Charset.forName("UTF-32LE") : Charset.forName("UTF-16LE");
+            } else {
+
+                    /*check BOM
+
+                    Encoding       hex byte order mark
+                    UTF-8          EF BB BF
+                    UTF-16 (BE)    FE FF
+                    UTF-16 (LE)    FF FE
+                    UTF-32 (BE)    00 00 FE FF
+                    UTF-32 (LE)    FF FE 00 00
+                    */
+
+                //We do not check for UTF-32BE because that is already covered above and we
+                //do not to unread anything.
+
+                if(first == 0xFE && second == 0xFF) {
+                    charset = Charset.forName("UTF-16BE");
+                    bomLength=2;
+                } else if(first == 0xFF && second == 0xFE) {
+                    if(utfBytes.length > 3 && (utfBytes[2]&0xff) == 0x00 && (utfBytes[3]&0xff) == 0x00) {
+                        charset = Charset.forName("UTF-32LE");
+                        bomLength=4;
+                    }else {
+                        charset = Charset.forName("UTF-16LE");
+                        bomLength=2;
+                    }
+                } else if (utfBytes.length > 2 && first == 0xEF && second == 0xBB && (utfBytes[2]&0xff) == 0xBF) {
+                    //UTF-8 with BOM
+                    bomLength=3;
+                }
+            }
+            //assume UTF8
+            if(bomLength > 0 && bomLength < 4) {             
+                //do not unread BOM, only bytes after BOM        
+                inputStream.unread(utfBytes,bomLength,utfBytes.length - bomLength);
+            } else {             
+                //no BOM, unread all read bytes
+                inputStream.unread(utfBytes);
+            }
+          
+
+        } catch (final IOException e) {
+            throw new JsonException("Unable to detect charset due to "+e.getMessage(), e);
+        }
+
+        return charset;
+    }
+
+}
diff --git a/src/main/java/org/apache/johnzon/core/SerializableValue.java b/src/main/java/org/apache/johnzon/core/SerializableValue.java
new file mode 100644
index 0000000..76b3ec6
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/SerializableValue.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import javax.json.JsonReader;
+import javax.json.JsonReaderFactory;
+import javax.json.spi.JsonProvider;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.io.StringReader;
+import java.util.Collections;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class SerializableValue implements Serializable {
+    private static final AtomicReference<JsonReaderFactory> FACTORY_ATOMIC_REFERENCE = new AtomicReference<JsonReaderFactory>();
+
+    private final String value;
+
+    SerializableValue(final String value) {
+        this.value = value;
+    }
+
+    private Object readResolve() throws ObjectStreamException {
+        final JsonReader parser = factory().createReader(new StringReader(value));
+        try {
+            return parser.read();
+        } finally {
+            parser.close();
+        }
+    }
+
+    private static JsonReaderFactory factory() { // avoid to create too much instances of provider or factories, not needed
+        JsonReaderFactory factory = FACTORY_ATOMIC_REFERENCE.get();
+        if (factory == null) {
+            FACTORY_ATOMIC_REFERENCE.compareAndSet(null, JsonProvider.provider().createReaderFactory(Collections.<String, Object>emptyMap()));
+            factory = FACTORY_ATOMIC_REFERENCE.get();
+        }
+        return factory;
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/SimpleStack.java b/src/main/java/org/apache/johnzon/core/SimpleStack.java
new file mode 100644
index 0000000..74bd14f
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/SimpleStack.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+class SimpleStack<T> {
+
+    private Element<T> head;
+
+    void push(final T element) {
+
+        final Element<T> tmp = new Element<T>();
+        tmp.payload = element;
+        tmp.previous = head;
+        head = tmp;
+
+    }
+
+    T pop() {
+
+        final T tmp = head.payload;
+        head = head.previous;
+        return tmp;
+
+    }
+
+    T peek() {
+        return head.payload;
+    }
+
+    boolean isEmpty() {
+        return head == null;
+    }
+
+    private static class Element<T> {
+
+        public Element() {
+
+        }
+
+        private Element<T> previous;
+        private T payload;
+
+    }
+
+}
diff --git a/src/main/java/org/apache/johnzon/core/Strings.java b/src/main/java/org/apache/johnzon/core/Strings.java
new file mode 100644
index 0000000..412f6ef
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/Strings.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.json.stream.JsonParsingException;
+
+class Strings implements JsonChars {
+    private static final BufferStrategy.BufferProvider<StringBuilder> BUILDER_CACHE =
+        BufferStrategy.valueOf(System.getProperty("johnzon.string-builder.strategy", "QUEUE"))
+            .newStringBuilderProvider(Integer.getInteger("org.apache.johnzon.default-string-builder", 1024));
+
+    private static final String UNICODE_PREFIX = "\\u";
+    private static final String UNICODE_PREFIX_HELPER = "000";
+    private static final ConcurrentMap<Character, String> UNICODE_CACHE = new ConcurrentHashMap<Character, String>();
+
+    static char asEscapedChar(final char current) {
+        switch (current) {
+            case 'r':
+                return '\r';
+            case 't':
+                return '\t';
+            case 'b':
+                return '\b';
+            case 'f':
+                return '\f';
+            case 'n':
+                return '\n';
+            case '"':
+                return '\"';
+            case '\\':
+                return '\\';  
+            case '/':
+                return '/';  
+            default:
+                if(Character.isHighSurrogate(current) || Character.isLowSurrogate(current)) {
+                    return current;
+                }
+                throw new JsonParsingException("Invalid escape sequence '"+current +"' (Codepoint: "+String.valueOf(current).
+                        codePointAt(0),JsonLocationImpl.UNKNOWN_LOCATION);
+        }
+
+    }
+
+    static String escape(final String value) {
+        
+        if(value == null || value.length()==0) {
+            return value;
+        }
+        
+        final StringBuilder builder = BUILDER_CACHE.newBuffer();
+        try {
+            for (int i = 0; i < value.length(); i++) {
+                final char c = value.charAt(i);
+                switch (c) {
+                    case QUOTE_CHAR:
+                    case ESCAPE_CHAR:
+                        builder.append(ESCAPE_CHAR).append(c);
+                        break;
+                    default:
+                        if (c < SPACE) { // we could do a single switch but actually we should rarely enter this if so no need to pay it
+                            switch (c) {
+                                case EOL:
+                                    builder.append("\\n");
+                                    break;
+                                case '\r':
+                                    builder.append("\\r");
+                                    break;
+                                case '\t':
+                                    builder.append("\\t");
+                                    break;
+                                case '\b':
+                                    builder.append("\\b");
+                                    break;
+                                case '\f':
+                                    builder.append("\\f");
+                                    break;
+                                default:
+                                    builder.append(toUnicode(c));
+                            }
+                        } else if ((c >= '\u0080' && c < '\u00a0') || (c >= '\u2000' && c < '\u2100')) {
+                            builder.append(toUnicode(c));
+                        } else {
+                            builder.append(c);
+                        }
+                }
+            }
+            return builder.toString();
+        } finally {
+            BUILDER_CACHE.release(builder);
+        }
+    }
+
+    private static String toUnicode(final char c) {
+        final String found = UNICODE_CACHE.get(c);
+        if (found != null) {
+            return found;
+        }
+
+        final String hex = UNICODE_PREFIX_HELPER + Integer.toHexString(c);
+        final String s = UNICODE_PREFIX + hex.substring(hex.length() - 4);
+        UNICODE_CACHE.putIfAbsent(c, s);
+        return s;
+    }
+
+    private Strings() {
+        // no-op
+    }
+}
diff --git a/src/main/java/org/apache/johnzon/core/ThreadLocalBufferCache.java b/src/main/java/org/apache/johnzon/core/ThreadLocalBufferCache.java
new file mode 100644
index 0000000..f973fcd
--- /dev/null
+++ b/src/main/java/org/apache/johnzon/core/ThreadLocalBufferCache.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.core;
+
+import java.io.Serializable;
+
+public abstract class ThreadLocalBufferCache<T> implements Serializable {
+    // alloc are expensive so using a very trivial buffer, we remove from TL to say we use it and reset it when we are done (close)
+    private final ThreadLocal<T> buffers;
+    private final int defaultSize;
+
+    public ThreadLocalBufferCache(final int defaultSize) {
+        this.buffers = new ThreadLocal<T>() {
+            @Override
+            protected T initialValue() {
+                return ThreadLocalBufferCache.this.newValue(defaultSize);
+            }
+        };
+        this.defaultSize = defaultSize;
+    }
+
+    protected abstract T newValue(final int defaultSize);
+
+    public T getCache() {
+        final T cachedLoadedChars = buffers.get();
+        if (cachedLoadedChars == null) {
+            return newValue(defaultSize);
+        }
+        buffers.set(null); // remove just kills the buffers
+        return cachedLoadedChars;
+    }
+
+
+    public void release(final T buffer) {
+        buffers.set(buffer);
+    }
+}

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.