fix the johnzon-core dependency in mapper/jsonb module + use BoundedOutputStreamWriter in Snippet
diff --git a/johnzon-core/src/main/java/org/apache/johnzon/core/IODescriptor.java b/johnzon-core/src/main/java/org/apache/johnzon/core/Buffered.java
similarity index 92%
rename from johnzon-core/src/main/java/org/apache/johnzon/core/IODescriptor.java
rename to johnzon-core/src/main/java/org/apache/johnzon/core/Buffered.java
index 1581ce0..40cabfb 100644
--- a/johnzon-core/src/main/java/org/apache/johnzon/core/IODescriptor.java
+++ b/johnzon-core/src/main/java/org/apache/johnzon/core/Buffered.java
@@ -26,7 +26,7 @@
  *
  * @since 1.2.17
  */
-public interface IODescriptor {
+public interface Buffered { // https://github.com/apache/johnzon/pull/84#discussion_r860563179 for the naming ;)
 
     /**
      * The buffer size used by this stream while reading input or before writing
diff --git a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonGeneratorFactoryImpl.java b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonGeneratorFactoryImpl.java
index 0e5fac2..646fea4 100644
--- a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonGeneratorFactoryImpl.java
+++ b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonGeneratorFactoryImpl.java
@@ -56,8 +56,9 @@
         super(config, SUPPORTED_CONFIG_KEYS, null);
         this.pretty = getBool(JsonGenerator.PRETTY_PRINTING, false);
         this.boundedOutputStreamWriter = getInt(BOUNDED_OUTPUT_STREAM_WRITER_LEN, -1);
-        this.defaultEncoding = ofNullable(getString(ENCODING, null))
-                .map(Charset::forName)
+        this.defaultEncoding = ofNullable(config)
+                .map(c -> c.get(ENCODING))
+                .map(it -> Charset.class.isInstance(it) ? Charset.class.cast(it) : Charset.forName(it.toString()))
                 .orElse(UTF_8);
 
         final int bufferSize = getInt(GENERATOR_BUFFER_LENGTH, DEFAULT_GENERATOR_BUFFER_LENGTH);
@@ -96,12 +97,16 @@
         return Collections.unmodifiableMap(internalConfig);
     }
 
+    public Charset getDefaultEncoding() {
+        return defaultEncoding;
+    }
+
     private BufferStrategy.BufferProvider<char[]> getBufferProvider(final Flushable flushable) {
-        if (!(flushable instanceof IODescriptor)) {
+        if (!(flushable instanceof Buffered)) {
             return buffer.provider;
         }
 
-        final int bufferSize = IODescriptor.class.cast(flushable).bufferSize();
+        final int bufferSize = Buffered.class.cast(flushable).bufferSize();
 
         if (customBuffer != null && customBuffer.size == bufferSize) {
             return customBuffer.provider;
diff --git a/johnzon-core/src/main/java/org/apache/johnzon/core/Snippet.java b/johnzon-core/src/main/java/org/apache/johnzon/core/Snippet.java
index 5df0b3a..23ed634 100644
--- a/johnzon-core/src/main/java/org/apache/johnzon/core/Snippet.java
+++ b/johnzon-core/src/main/java/org/apache/johnzon/core/Snippet.java
@@ -16,6 +16,9 @@
  */
 package org.apache.johnzon.core;
 
+import org.apache.johnzon.core.io.BoundedOutputStreamWriter;
+
+import javax.json.Json;
 import javax.json.JsonArray;
 import javax.json.JsonObject;
 import javax.json.JsonValue;
@@ -24,13 +27,11 @@
 import java.io.ByteArrayOutputStream;
 import java.io.Closeable;
 import java.io.IOException;
-import java.io.OutputStreamWriter;
 import java.io.Writer;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.function.Supplier;
 
-import static org.apache.johnzon.core.JsonGeneratorFactoryImpl.GENERATOR_BUFFER_LENGTH;
+import static java.nio.charset.StandardCharsets.UTF_8;
 
 /**
  * Constructs short snippets of serialized JSON text representations of
@@ -41,26 +42,9 @@
  * is therefore safe to use regardless of the size of the JsonValue.
  */
 public class Snippet {
-
     private final int max;
     private final JsonGeneratorFactory generatorFactory;
 
-    // IMPORTANT: should NOT be used inside johnzon project itself which should *always*
-    //            use a contextual JsonGeneratorFactory - keep in mind we are not johnzon-core dependent
-    //            not JSON dependent (some JsonGeneratorFactory can issue yaml or binary for ex)
-    /**
-     * This constructor should be used only in static or other scenarios were
-     * there is no JsonGeneratorFactory instance in scope.
-     *
-     * @param max the maximum length of the serialized json produced via of()
-     */
-    public Snippet(final int max) {
-        this(max, new JsonGeneratorFactoryImpl(new HashMap<String, Object>() {
-            {
-                this.put(GENERATOR_BUFFER_LENGTH, max);
-            }
-        }));
-    }
 
     /**
      * This is the preferred approach to using Snippet in any context where
@@ -85,10 +69,27 @@
      * @return a potentially truncated json text
      */
     public String of(final JsonValue value) {
-        try (final Buffer buffer = new Buffer()) {
-            buffer.write(value);
-            return buffer.get();
+        final Buffer buffer = new Buffer();
+        try (final Buffer b = buffer) {
+            b.write(value);
         }
+        return buffer.get();
+    }
+
+    // IMPORTANT: should NOT be used inside johnzon project itself which should *always*
+    //            use a contextual JsonGeneratorFactory - keep in mind we are not johnzon-core dependent
+    //            not JSON dependent (some JsonGeneratorFactory can issue yaml or binary for ex)
+    /**
+     * This factory should be used only in static or other scenarios were
+     * there is no JsonGeneratorFactory instance in scope - ie external code.
+     *
+     * @param max the maximum length of the serialized json produced via of()
+     */
+    public static Snippet of(final int max) {
+        final Map<String, Object> properties = new HashMap<>();
+        properties.put("org.apache.johnzon.default-char-buffer-generator", max);
+        properties.put("org.apache.johnzon.boundedoutputstreamwriter", max);
+        return new Snippet(max, Json.createGeneratorFactory(properties));
     }
 
     /**
@@ -102,12 +103,58 @@
      * is a JsonGeneratorFactory instance in scope. For those scenarios
      * use the constructor that accepts a JsonGeneratorFactory instead.
      *
-     * @param value the JsonValue to be serialized as json text
      * @param max the maximum length of the serialized json text
+     * @param value the JsonValue to be serialized as json text
      * @return a potentially truncated json text
      */
-    public static String of(final JsonValue value, final int max) {
-        return new Snippet(max).of(value);
+    public static String of(final int max, final JsonValue value) {
+        return of(max).of(value);
+    }
+
+    // skips some methods using a buffer
+    private static abstract class PassthroughWriter extends Writer {
+        @Override
+        public void write(final char[] cbuf) throws IOException {
+            write(cbuf, 0, cbuf.length);
+        }
+
+        @Override
+        public void write(final String str) throws IOException {
+            write(str.toCharArray(), 0, str.length());
+        }
+
+        @Override
+        public void write(final String str, final int off, final int len) throws IOException {
+            write(str.toCharArray(), 0, len);
+        }
+
+        @Override
+        public Writer append(final CharSequence csq) throws IOException {
+            write(csq.toString().toCharArray(), 0, csq.length());
+            return this;
+        }
+
+        @Override
+        public Writer append(final CharSequence csq, final int start, final int end) throws IOException {
+            write(csq.toString().toCharArray(), start, end);
+            return this;
+        }
+
+        @Override
+        public Writer append(final char c) throws IOException {
+            write(new char[]{c}, 0, 1);
+            return this;
+        }
+
+        @Override
+        public void flush() throws IOException {
+            // no-op
+        }
+
+        @Override
+        public void close() throws IOException {
+            // no-op
+        }
     }
 
     /**
@@ -126,15 +173,13 @@
      * need 50 bytes.  We could potentially optimize this code so the
      * buffer held by JsonGeneratorImpl is also the maxSnippetLength.
      */
-    class Buffer implements Closeable {
+    private class Buffer implements Closeable {
         private final JsonGenerator generator;
         private final SnippetWriter snippet;
-        private Runnable flush;
 
         private Buffer() {
             this.snippet = new SnippetWriter(max);
             this.generator = generatorFactory.createGenerator(snippet);
-            this.flush = generator::flush;
         }
 
         private void write(final JsonValue value) {
@@ -222,13 +267,11 @@
         }
 
         private boolean terminate() {
-            flush.run();
             return snippet.terminate();
         }
 
         private String get() {
-            generator.flush();
-            return snippet.isTruncated() ? snippet.get() + "..." : snippet.get();
+            return snippet.get() + (snippet.isTruncated() ? "..." : "");
         }
 
         @Override
@@ -249,30 +292,20 @@
          * If the last write brought us over the max length, the
          * state will be Truncated.
          */
-        class SnippetWriter extends Writer implements IODescriptor {
-
+        class SnippetWriter extends PassthroughWriter implements Buffered {
             private final ByteArrayOutputStream buffer;
-            private Mode mode;
-            private Supplier<Integer> bufferSize;
+            private final int max;
+            private PassthroughWriter mode;
 
             public SnippetWriter(final int max) {
-                final int size = Math.min(max, 8192);
-
-                this.buffer = new ByteArrayOutputStream(size);
-                this.mode = new Writing(max, new OutputStreamWriter(buffer));
-
-                /*
-                 * The first time the buffer size is requested, disable flushing
-                 * as we know our requested buffer size will be respected
-                 */
-                this.bufferSize = () -> {
-                    // disable flushing
-                    flush = () -> {
-                    };
-                    // future calls can just return the size
-                    bufferSize = () -> size;
-                    return size;
-                };
+                this.max = max;
+                this.buffer = new ByteArrayOutputStream(max);
+                this.mode = new Writing(max, new BoundedOutputStreamWriter(
+                        buffer,
+                        JsonGeneratorFactoryImpl.class.isInstance(generatorFactory) ?
+                                JsonGeneratorFactoryImpl.class.cast(generatorFactory).getDefaultEncoding() :
+                                UTF_8,
+                        max));
             }
 
             public String get() {
@@ -281,7 +314,7 @@
 
             @Override
             public int bufferSize() {
-                return bufferSize.get();
+                return max;
             }
 
             /**
@@ -325,17 +358,7 @@
                 mode.close();
             }
 
-            abstract class Mode extends Writer {
-                @Override
-                public void flush() throws IOException {
-                }
-
-                @Override
-                public void close() throws IOException {
-                }
-            }
-
-            class Writing extends Mode {
+            class Writing extends PassthroughWriter {
                 private final int max;
                 private int count;
                 private final Writer writer;
@@ -381,7 +404,7 @@
                     writer.close();
                 }
 
-                private void maxReached(final Mode mode) throws IOException {
+                private void maxReached(final PassthroughWriter mode) throws IOException {
                     SnippetWriter.this.mode = mode;
                     writer.flush();
                     writer.close();
@@ -392,8 +415,7 @@
              * Signifies the last write was fully written, but there is
              * no more space for future writes.
              */
-            class Completed extends Mode {
-
+            class Completed extends PassthroughWriter {
                 @Override
                 public void write(final char[] cbuf, final int off, final int len) throws IOException {
                     if (len > 0) {
@@ -406,9 +428,10 @@
              * Signifies the last write was not completely written and there was
              * no more space for this or future writes.
              */
-            class Truncated extends Mode {
+            class Truncated extends PassthroughWriter {
                 @Override
                 public void write(final char[] cbuf, final int off, final int len) throws IOException {
+                    // no-op
                 }
             }
         }
diff --git a/johnzon-core/src/main/java/org/apache/johnzon/core/Types.java b/johnzon-core/src/main/java/org/apache/johnzon/core/Types.java
index d06d27f..f7fb6a0 100644
--- a/johnzon-core/src/main/java/org/apache/johnzon/core/Types.java
+++ b/johnzon-core/src/main/java/org/apache/johnzon/core/Types.java
@@ -45,21 +45,6 @@
         return new ParameterizedTypeImpl(parameterizedClass, resolveArgumentTypes(klass, parameterizedClass));

     }

 

-    public Class<?> findParamType(final ParameterizedType type, final Class<?> expectedWrapper) {

-        if (type.getActualTypeArguments().length != 1) {

-            return null;

-        }

-        final Class<?> asClass = asClass(type.getRawType());

-        if (asClass == null || !expectedWrapper.isAssignableFrom(asClass)) {

-            return null;

-        }

-        return asClass(type.getActualTypeArguments()[0]);

-    }

-

-    public Class<?> asClass(final Type type) {

-        return Class.class.isInstance(type) ? Class.class.cast(type) : null;

-    }

-

     private Type[] resolveArgumentTypes(Type type, Class<?> parameterizedClass) {

         if (type instanceof Class<?>) {

             return resolveArgumentTypes((Class<?>) type, parameterizedClass);

diff --git a/johnzon-core/src/test/java/org/apache/johnzon/core/SnippetTest.java b/johnzon-core/src/test/java/org/apache/johnzon/core/SnippetTest.java
index 9d008ff..017d332 100644
--- a/johnzon-core/src/test/java/org/apache/johnzon/core/SnippetTest.java
+++ b/johnzon-core/src/test/java/org/apache/johnzon/core/SnippetTest.java
@@ -277,7 +277,7 @@
              * Serialize the json value, truncating nothing
              */
             final TrackingJsonGeneratorFactory full = new TrackingJsonGeneratorFactory();
-            new Snippet(Integer.MAX_VALUE, full).of(object);
+            new Snippet(8192, full).of(object);
 
             /*
              * Assert that the calls made in truncated version are less
diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java
index 5afe718..2e2f2a4 100644
--- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java
+++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java
@@ -18,15 +18,12 @@
  */
 package org.apache.johnzon.jsonb;
 
-import org.apache.johnzon.core.AbstractJsonFactory;
-import org.apache.johnzon.core.JsonGeneratorFactoryImpl;
-import org.apache.johnzon.core.JsonParserFactoryImpl;
-import org.apache.johnzon.core.Types;
 import org.apache.johnzon.jsonb.adapter.JsonbEnumAdapter;
 import org.apache.johnzon.jsonb.api.experimental.PolymorphicConfig;
 import org.apache.johnzon.jsonb.cdi.CDIs;
 import org.apache.johnzon.jsonb.converter.JohnzonJsonbAdapter;
 import org.apache.johnzon.jsonb.factory.SimpleJohnzonAdapterFactory;
+import org.apache.johnzon.jsonb.reflect.Types;
 import org.apache.johnzon.jsonb.serializer.JohnzonDeserializationContext;
 import org.apache.johnzon.jsonb.serializer.JohnzonSerializationContext;
 import org.apache.johnzon.jsonb.spi.JohnzonAdapterFactory;
@@ -442,8 +439,9 @@
         if (config == null) {
             return map;
         }
-        config.getProperty(JsonGeneratorFactoryImpl.GENERATOR_BUFFER_LENGTH).ifPresent(b -> map.put(JsonGeneratorFactoryImpl.GENERATOR_BUFFER_LENGTH, b));
-        config.getProperty(AbstractJsonFactory.BUFFER_STRATEGY).ifPresent(b -> map.put(AbstractJsonFactory.BUFFER_STRATEGY, b));
+        config.getProperty("org.apache.johnzon.default-char-buffer-generator").ifPresent(b -> map.put("org.apache.johnzon.default-char-buffer-generator", b));
+        config.getProperty("org.apache.johnzon.boundedoutputstreamwriter").ifPresent(b -> map.put("org.apache.johnzon.boundedoutputstreamwriter", b));
+        config.getProperty("org.apache.johnzon.buffer-strategy").ifPresent(b -> map.put("org.apache.johnzon.buffer-strategy", b));
         config.getProperty(JsonbConfig.FORMATTING).ifPresent(b -> map.put(JsonGenerator.PRETTY_PRINTING, b));
         return map;
     }
@@ -453,10 +451,10 @@
         if (config == null) {
             return map;
         }
-        config.getProperty(JsonParserFactoryImpl.BUFFER_LENGTH).ifPresent(b -> map.put(JsonParserFactoryImpl.BUFFER_LENGTH, b));
-        config.getProperty(JsonParserFactoryImpl.MAX_STRING_LENGTH).ifPresent(b -> map.put(JsonParserFactoryImpl.MAX_STRING_LENGTH, b));
-        config.getProperty(JsonParserFactoryImpl.SUPPORTS_COMMENTS).ifPresent(b -> map.put(JsonParserFactoryImpl.SUPPORTS_COMMENTS, b));
-        config.getProperty(AbstractJsonFactory.BUFFER_STRATEGY).ifPresent(b -> map.put(AbstractJsonFactory.BUFFER_STRATEGY, b));
+        config.getProperty("org.apache.johnzon.default-char-buffer").ifPresent(b -> map.put("org.apache.johnzon.default-char-buffer", b));
+        config.getProperty("org.apache.johnzon.max-string-length").ifPresent(b -> map.put("org.apache.johnzon.max-string-length", b));
+        config.getProperty("org.apache.johnzon.supports-comments").ifPresent(b -> map.put("org.apache.johnzon.supports-comments", b));
+        config.getProperty("org.apache.johnzon.buffer-strategy").ifPresent(b -> map.put("org.apache.johnzon.buffer-strategy", b));
         return map;
     }
 
diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java
index fbcb4a8..a0d0d8c 100644
--- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java
+++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java
@@ -18,7 +18,6 @@
  */
 package org.apache.johnzon.jsonb;
 
-import org.apache.johnzon.core.Types;
 import org.apache.johnzon.jsonb.converter.JohnzonJsonbAdapter;
 import org.apache.johnzon.jsonb.converter.JsonbDateConverter;
 import org.apache.johnzon.jsonb.converter.JsonbLocalDateConverter;
@@ -29,6 +28,7 @@
 import org.apache.johnzon.jsonb.converter.JsonbZonedDateTimeConverter;
 import org.apache.johnzon.jsonb.order.PerHierarchyAndLexicographicalOrderFieldComparator;
 import org.apache.johnzon.jsonb.reflect.GenericArrayTypeImpl;
+import org.apache.johnzon.jsonb.reflect.Types;
 import org.apache.johnzon.jsonb.serializer.JohnzonDeserializationContext;
 import org.apache.johnzon.jsonb.serializer.JohnzonSerializationContext;
 import org.apache.johnzon.jsonb.spi.JohnzonAdapterFactory;
diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/reflect/Types.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/reflect/Types.java
new file mode 100644
index 0000000..09775ce
--- /dev/null
+++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/reflect/Types.java
@@ -0,0 +1,157 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements. See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership. The ASF licenses this file

+ * to you under the Apache License, Version 2.0 (the

+ * "License"); you may not use this file except in compliance

+ * with the License. You may obtain a copy of the License at

+ *

+ * http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing,

+ * software distributed under the License is distributed on an

+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

+ * KIND, either express or implied. See the License for the

+ * specific language governing permissions and limitations

+ * under the License.

+ */

+package org.apache.johnzon.jsonb.reflect;

+

+import java.lang.reflect.ParameterizedType;

+import java.lang.reflect.Type;

+import java.lang.reflect.TypeVariable;

+import java.util.Arrays;

+

+// forked from johnzon-core to ensure the independency without creating a new module just for that

+public class Types {

+    public ParameterizedType findParameterizedType(final Class<?> klass, final Class<?> parameterizedClass) {

+        return new ParameterizedTypeImpl(parameterizedClass, resolveArgumentTypes(klass, parameterizedClass));

+    }

+

+    public Class<?> findParamType(final ParameterizedType type, final Class<?> expectedWrapper) {

+        if (type.getActualTypeArguments().length != 1) {

+            return null;

+        }

+        final Class<?> asClass = asClass(type.getRawType());

+        if (asClass == null || !expectedWrapper.isAssignableFrom(asClass)) {

+            return null;

+        }

+        return asClass(type.getActualTypeArguments()[0]);

+    }

+

+    public Class<?> asClass(final Type type) {

+        return Class.class.isInstance(type) ? Class.class.cast(type) : null;

+    }

+

+    private Type[] resolveArgumentTypes(final Type type, final Class<?> parameterizedClass) {

+        if (type instanceof Class<?>) {

+            return resolveArgumentTypes((Class<?>) type, parameterizedClass);

+        }

+        if (type instanceof ParameterizedType) {

+            return resolveArgumentTypes((ParameterizedType) type, parameterizedClass);

+        }

+        throw new IllegalArgumentException("Cannot resolve argument types from " + type.getClass().getSimpleName());

+    }

+

+    private Type[] resolveArgumentTypes(final Class<?> type, final Class<?> parameterizedClass) {

+        if (parameterizedClass.equals(type)) {

+            // May return Class[] instead of Type[], so copy it as a Type[] to avoid

+            // problems in visit(ParameterizedType)

+            return Arrays.copyOf(type.getTypeParameters(), parameterizedClass.getTypeParameters().length, Type[].class);

+        }

+        if (type.getSuperclass() != null && parameterizedClass.isAssignableFrom(type.getSuperclass())) {

+            return resolveArgumentTypes(type.getGenericSuperclass(), parameterizedClass);

+        }

+        Class<?>[] interfaces = type.getInterfaces();

+        Type[] genericInterfaces = type.getGenericInterfaces();

+        for (int i = 0; i < interfaces.length; i++) {

+            if (parameterizedClass.isAssignableFrom(interfaces[i])) {

+                return resolveArgumentTypes(genericInterfaces[i], parameterizedClass);

+            }

+        }

+        throw new IllegalArgumentException(String.format("%s is not assignable from %s", type, parameterizedClass));

+    }

+

+    private Type[] resolveArgumentTypes(final ParameterizedType type, final Class<?> parameterizedClass) {

+        final Class<?> rawType = (Class<?>) type.getRawType(); // always a Class

+        final TypeVariable<?>[] typeVariables = rawType.getTypeParameters();

+        final Type[] types = resolveArgumentTypes(rawType, parameterizedClass);

+        for (int i = 0; i < types.length; i++) {

+            if (types[i] instanceof TypeVariable<?>) {

+                TypeVariable<?> typeVariable = (TypeVariable<?>) types[i];

+                for (int j = 0; j < typeVariables.length; j++) {

+                    if (typeVariables[j].getName().equals(typeVariable.getName())) {

+                        types[i] = type.getActualTypeArguments()[j];

+                    }

+                }

+            }

+        }

+        return types;

+    }

+

+    private static class ParameterizedTypeImpl implements ParameterizedType {

+        private final Type rawType;

+        private final Type[] arguments;

+

+        private ParameterizedTypeImpl(final Type rawType, final Type... arguments) {

+            this.rawType = rawType;

+            this.arguments = arguments;

+        }

+

+        @Override

+        public Type getRawType() {

+            return rawType;

+        }

+

+        @Override

+        public Type getOwnerType() {

+            return null;

+        }

+

+        @Override

+        public Type[] getActualTypeArguments() {

+            return arguments;

+        }

+

+        @Override

+        public int hashCode() {

+            return Arrays.hashCode(arguments) ^ (rawType == null ? 0 : rawType.hashCode());

+        }

+

+        @Override

+        public boolean equals(final Object obj) {

+            if (this == obj) {

+                return true;

+            }

+            if (obj instanceof ParameterizedType) {

+                final ParameterizedType that = (ParameterizedType) obj;

+                final Type thatRawType = that.getRawType();

+                return that.getOwnerType() == null

+                        && (rawType == null ? thatRawType == null : rawType.equals(thatRawType))

+                        && Arrays.equals(arguments, that.getActualTypeArguments());

+            }

+            return false;

+        }

+

+        @Override

+        public String toString() {

+            final StringBuilder buffer = new StringBuilder();

+            buffer.append(((Class<?>) rawType).getSimpleName());

+            final Type[] actualTypes = getActualTypeArguments();

+            if (actualTypes.length > 0) {

+                buffer.append("<");

+                int length = actualTypes.length;

+                for (int i = 0; i < length; i++) {

+                    buffer.append(actualTypes[i].toString());

+                    if (i != actualTypes.length - 1) {

+                        buffer.append(",");

+                    }

+                }

+

+                buffer.append(">");

+            }

+            return buffer.toString();

+        }

+    }

+}

diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JohnzonCores.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JohnzonCores.java
index 91e0e2d..76a0e35 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JohnzonCores.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JohnzonCores.java
@@ -18,14 +18,15 @@
  */
 package org.apache.johnzon.mapper;
 
-import static java.util.Optional.ofNullable;
-
+import javax.json.JsonReader;
+import javax.json.JsonReaderFactory;
+import javax.json.JsonValue;
+import javax.json.stream.JsonGeneratorFactory;
+import javax.json.stream.JsonParser;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 
-import javax.json.JsonReader;
-import javax.json.JsonReaderFactory;
-import javax.json.stream.JsonParser;
+import static java.util.Optional.ofNullable;
 
 public final class JohnzonCores {
     private static final Method CREATE_READER;
@@ -50,7 +51,17 @@
         // no-op
     }
 
+    public static SnippetFactory snippetFactory(final int max, final JsonGeneratorFactory factory) {
+        if (CREATE_READER == null) {
+            return JsonValue::toString;
+        }
+        return Snippets.of(max, factory);
+    }
+
     public static JsonReader map(final JsonParser parser, final JsonReaderFactory readerFactory) {
+        if (CREATE_READER == null) {
+            throw new IllegalStateException("Ensure to use johnzon-core as JSON-P provider for johnzon-mapper");
+        }
         try {
             return JsonReader.class.cast(CREATE_READER.invoke(readerFactory, parser));
         } catch (final IllegalAccessException e) {
@@ -59,4 +70,15 @@
             throw new IllegalStateException(e.getTargetException());
         }
     }
+
+    // indirection (for classloading)
+    private static class Snippets {
+        private Snippets() {
+            // no-op
+        }
+
+        private static SnippetFactory of(final int max, final JsonGeneratorFactory factory) {
+            return new org.apache.johnzon.core.Snippet(max, factory)::of;
+        }
+    }
 }
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JohnzonVirtualObject.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JohnzonVirtualObject.java
index 6cd1aae..dd8f373 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JohnzonVirtualObject.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JohnzonVirtualObject.java
@@ -22,8 +22,6 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
-import org.apache.johnzon.core.Experimental;
-
 import static java.lang.annotation.ElementType.TYPE;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
@@ -34,7 +32,6 @@
 @Target(TYPE)
 @Retention(RUNTIME)
 @Inherited
-@Experimental
 public @interface JohnzonVirtualObject {
     /**
      * @return the virtual object(s) path.
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java
index fccb9fd..ef4ca51 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java
@@ -23,7 +23,6 @@
 import static java.util.Locale.ROOT;
 
 // import org.apache.johnzon.core.JsonParserFactoryImpl; // don't depend on core in mapper
-import org.apache.johnzon.core.Snippet;
 import org.apache.johnzon.mapper.access.AccessMode;
 import org.apache.johnzon.mapper.access.BaseAccessMode;
 import org.apache.johnzon.mapper.access.FieldAccessMode;
@@ -130,7 +129,7 @@
                 config.put("org.apache.johnzon.buffer-strategy", bufferStrategy);
             }
             if (encoding != null) {
-                config.put("org.apache.johnzon.encoding", encoding.name());
+                config.put("org.apache.johnzon.encoding", encoding);
             }
 
             if (generatorFactory == null) {
@@ -239,7 +238,7 @@
                         typeLoader, discriminatorMapper, discriminator,
                         deserializationPredicate, serializationPredicate,
                         enumConverterFactory,
-                        new Snippet(snippetMaxLength, generatorFactory)),
+                        JohnzonCores.snippetFactory(snippetMaxLength, generatorFactory)),
                 closeables);
     }
 
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java
index eb8affc..824c670 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperConfig.java
@@ -18,7 +18,6 @@
  */
 package org.apache.johnzon.mapper;
 
-import org.apache.johnzon.core.Snippet;
 import org.apache.johnzon.mapper.access.AccessMode;
 import org.apache.johnzon.mapper.internal.AdapterKey;
 import org.apache.johnzon.mapper.internal.ConverterAdapter;
@@ -97,7 +96,7 @@
 
     private final Function<Class<?>, CustomEnumConverter<?>> enumConverterFactory;
 
-    private final Snippet snippet;
+    private final SnippetFactory snippet;
 
     //CHECKSTYLE:OFF
     @Deprecated
@@ -130,7 +129,7 @@
                 attributeOrder, failOnUnknown, serializeValueFilter, useBigDecimalForFloats, deduplicateObjects, interfaceImplementationMapping,
                 useJsRange, useBigDecimalForObjectNumbers, supportEnumMapDeserialization, typeLoader,
                 discriminatorMapper, discriminator, deserializationPredicate, serializationPredicate, enumConverterFactory,
-                new Snippet(50, Json.createGeneratorFactory(emptyMap())));
+                JohnzonCores.snippetFactory(50, Json.createGeneratorFactory(emptyMap())));
     }
 
     //disable checkstyle for 10+ parameters
@@ -158,7 +157,7 @@
                         final Predicate<Class<?>> deserializationPredicate,
                         final Predicate<Class<?>> serializationPredicate,
                         final Function<Class<?>, CustomEnumConverter<?>> enumConverterFactory,
-                        final Snippet snippet) {
+                        final SnippetFactory snippet) {
     //CHECKSTYLE:ON
         this.objectConverterWriters = objectConverterWriters;
         this.objectConverterReaders = objectConverterReaders;
@@ -199,7 +198,7 @@
         this.snippet = snippet;
     }
 
-    public Snippet getSnippet() {
+    public SnippetFactory getSnippet() {
         return snippet;
     }
 
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/SnippetFactory.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/SnippetFactory.java
new file mode 100644
index 0000000..a4d057a
--- /dev/null
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/SnippetFactory.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.mapper;
+
+import javax.json.JsonValue;
+
+public interface SnippetFactory {
+    String of(JsonValue value);
+}
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/internal/JsonPointerTracker.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/internal/JsonPointerTracker.java
index fe376c0..c6d3495 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/internal/JsonPointerTracker.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/internal/JsonPointerTracker.java
@@ -18,21 +18,14 @@
  */
 package org.apache.johnzon.mapper.internal;
 
-import org.apache.johnzon.core.JsonPointerUtil;
-
 /**
  * Internal class to easily collect information about the 'depth' of a json object
  * without having to eagerly construct it.
- *
+ * <p>
  * For use in recursive generator and parser method calls to defer string operations.
  */
 public class JsonPointerTracker {
-    public static final JsonPointerTracker ROOT = new JsonPointerTracker(null, null) {
-        @Override
-        public String toString() {
-            return "/";
-        }
-    };
+    public static final JsonPointerTracker ROOT = new JsonPointerTracker(null, null);
 
     private final JsonPointerTracker parent;
     private final String currentNode;
@@ -41,7 +34,7 @@
 
 
     /**
-     * @param parent or {@code null} if this is the root object
+     * @param parent      or {@code null} if this is the root object
      * @param currentNode the name of the attribute or "/" for the root object
      */
     public JsonPointerTracker(JsonPointerTracker parent, String currentNode) {
@@ -51,28 +44,44 @@
 
     /**
      * For Arrays and Lists.
+     *
      * @param jsonPointer
-     * @param i current counter number
+     * @param i           current counter number
      */
     public JsonPointerTracker(JsonPointerTracker jsonPointer, int i) {
-       this(jsonPointer, Integer.toString(i));
+        this(jsonPointer, Integer.toString(i));
     }
 
     @Override
     public String toString() {
-        if (jsonPointer == null) {
-            if (parent != null) {
-                jsonPointer = (parent != ROOT ? parent + "/" : "/") + JsonPointerUtil.encode(currentNode);
-            } else {
-                if (currentNode != null) {
-                    jsonPointer = "/" + JsonPointerUtil.encode(currentNode);
-                } else {
-                    jsonPointer = "/";
-                }
-            }
+        if (jsonPointer != null) {
+            return jsonPointer;
         }
-
-        return jsonPointer;
+        if (parent != null) {
+            return jsonPointer = (parent != ROOT ? parent + "/" : "/") + encode(currentNode);
+        }
+        if (currentNode != null) {
+            return jsonPointer = "/" + encode(currentNode);
+        }
+        return jsonPointer = "/";
     }
 
+    // from org.apache.johnzon.mapper.internal.JsonPointerTracker to not depend on johnzon-core
+    private static String encode(final String s) {
+        if (s == null || s.isEmpty()) {
+            return s;
+        }
+        return replace(replace(s, "~", "~0"), "/", "~1");
+    }
+
+    private static String replace(final String src, final String from, final String to) {
+        if (src.isEmpty()) {
+            return src;
+        }
+        final int start = src.indexOf(from);
+        if (start >= 0) {
+            return src.substring(0, start) + to + replace(src.substring(start + from.length()), from, to);
+        }
+        return src;
+    }
 }