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

struberg pushed a commit to branch johnzon-1.2.x
in repository https://gitbox.apache.org/repos/asf/johnzon.git


The following commit(s) were added to refs/heads/johnzon-1.2.x by this push:
     new 276171b6 JOHNZON-421 Defer start of object/array
276171b6 is described below

commit 276171b61e81296401a1d57668b45f56a42855ac
Author: Mark Struberg <[email protected]>
AuthorDate: Wed Feb 11 15:00:46 2026 +0100

    JOHNZON-421 Defer start of object/array
    
    This is necessary as we don't know what Serializers/Adapters/Converters 
actually do write.
    If we start an object with { and the JsonbSerializer just writes a string 
value (json primitive)
    then we will blow up. Deferring the start fixes this issue.
---
 .../org/apache/johnzon/jsonb/JohnzonBuilder.java   |   4 +-
 .../org/apache/johnzon/jsonb/JsonbAccessMode.java  |  83 ++++-
 .../johnzon/jsonb/JohnzonConverterInJsonbTest.java |   5 +-
 .../org/apache/johnzon/jsonb/SerializerTest.java   |   2 +-
 .../johnzon/jsonb/SerializersRoundTripTest.java    |   3 +-
 .../SerialiseAsPrimitiveAnnotatedAtClassTest.java  | 129 ++++++++
 .../johnzon/mapper/DynamicMappingGenerator.java    |  43 +--
 .../java/org/apache/johnzon/mapper/Mapper.java     |   2 +-
 .../org/apache/johnzon/mapper/MapperConfig.java    |   3 +-
 .../apache/johnzon/mapper/MappingGenerator.java    |   7 +-
 .../johnzon/mapper/MappingGeneratorImpl.java       | 120 +++----
 .../org/apache/johnzon/mapper/ObjectConverter.java |   3 +-
 .../mapper/jsonp/DeferredStartJsonGenerator.java   | 354 +++++++++++++++++++++
 .../apache/johnzon/mapper/MapperConfigTest.java    |   5 +-
 .../mapper/ObjectConverterWithAnnotationTest.java  |   7 +-
 .../org/apache/johnzon/mapper/ObjectTypeTest.java  |  11 +-
 16 files changed, 651 insertions(+), 130 deletions(-)

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 3cab08e2..3e76eea8 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
@@ -309,9 +309,9 @@ public class JohnzonBuilder implements JsonbBuilder {
                     throw new IllegalArgumentException("We only support 
serializer on Class for now");
                 }
                 builder.addObjectConverter(
-                    Class.class.cast(args[0]), (ObjectConverter.Writer) 
(instance, jsonbGenerator) ->
+                    Class.class.cast(args[0]), (ObjectConverter.Writer) 
(instance, jsonbGenerator, generator) ->
                         s.serialize(
-                                instance, jsonbGenerator.getJsonGenerator(),
+                                instance, generator,
                                 new 
JohnzonSerializationContext(jsonbGenerator)));
             });
         });
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 49411f56..f9835ade 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
@@ -264,7 +264,8 @@ public class JsonbAccessMode implements AccessMode, 
Closeable, Cleanable<Class<?
                 final JsonbDateFormat dateFormat = getAnnotation(parameter, 
JsonbDateFormat.class);
                 final JsonbNumberFormat numberFormat = 
getAnnotation(parameter, JsonbNumberFormat.class);
                 final JohnzonConverter johnzonConverter = 
getAnnotation(parameter, JohnzonConverter.class);
-                if (adapter == null && dateFormat == null && numberFormat == 
null && johnzonConverter == null) {
+                final JsonbTypeDeserializer deserializer = 
getAnnotation(parameter, JsonbTypeDeserializer.class);
+                if (adapter == null && dateFormat == null && numberFormat == 
null && johnzonConverter == null && deserializer == null) {
                     converters[i] = defaultConverters.get(parameter.getType());
                     itemConverters[i] = null;
                 } else {
@@ -283,6 +284,63 @@ public class JsonbAccessMode implements AccessMode, 
Closeable, Cleanable<Class<?
                             }
                         } else if (johnzonConverter != null) {
                             objectConverters[i] = (ObjectConverter.Codec<?>) 
johnzonConverter.value().newInstance();
+
+                        } else if (deserializer != null) {
+                            final Class<? extends JsonbDeserializer> value = 
deserializer.value();
+                            final JohnzonAdapterFactory.Instance<? extends 
JsonbDeserializer> instance = newInstance(value);
+                            final ParameterizedType pt = 
this.types.findParameterizedType(value, JsonbDeserializer.class);
+                            final Class<?> mappedType = 
this.types.findParamType(pt, JsonbDeserializer.class);
+                            toRelease.add(instance);
+                            final JsonBuilderFactory builderFactoryInstance = 
builderFactory.get();
+                            final Type[] arguments = 
this.types.findParameterizedType(value, 
JsonbDeserializer.class).getActualTypeArguments();
+                            final boolean global = arguments.length == 1 && 
arguments[0] != null && arguments[0].equals(parameter.getType());
+                            objectConverters[i] = new ObjectConverter.Codec() {
+                                private final ConcurrentMap<Type, 
BiFunction<JsonValue, MappingParser, Object>> impl =
+                                    new ConcurrentHashMap<>();
+                                @Override
+                                public Object fromJson(final JsonValue value, 
final Type targetType, final MappingParser parser) {
+                                    final JsonbDeserializer jsonbDeserializer 
= instance.getValue();
+                                    if (global || targetType == mappedType) { 
// fast test and matches most cases
+                                        return mapItem(value, targetType, 
parser, jsonbDeserializer);
+                                    }
+
+                                    BiFunction<JsonValue, MappingParser, 
Object> fn = impl.get(targetType);
+                                    if (fn == null) {
+                                        if (value.getValueType() == 
JsonValue.ValueType.ARRAY) {
+                                            if 
(ParameterizedType.class.isInstance(targetType)) {
+                                                final ParameterizedType 
parameterizedType = ParameterizedType.class.cast(targetType);
+                                                final Class<?> paramType = 
JsonbAccessMode.this.types.findParamType(parameterizedType, Collection.class);
+                                                if (paramType != null && 
(mappedType == null /*Object*/ || mappedType.isAssignableFrom(paramType))) {
+                                                    final Collector<Object, ?, 
? extends Collection<Object>> collector =
+                                                        
Set.class.isAssignableFrom(
+                                                            
JsonbAccessMode.this.types.asClass(parameterizedType.getRawType())) ? toSet() : 
toList();
+                                                    fn = (json, mp) -> 
json.asJsonArray().stream()
+                                                                           
.map(i -> mapItem(i, paramType, mp, jsonbDeserializer))
+                                                                           
.collect(collector);
+                                                }
+                                            }
+                                        }
+                                        if (fn == null) {
+                                            fn = (json, mp) -> mapItem(json, 
targetType, mp, jsonbDeserializer);
+                                        }
+                                        impl.putIfAbsent(targetType, fn);
+                                    }
+                                    return fn.apply(value, parser);
+                                }
+
+                                private Object mapItem(final JsonValue 
jsonValue, final Type targetType,
+                                                       final MappingParser 
parser, final JsonbDeserializer jsonbDeserializer) {
+                                    return jsonbDeserializer.deserialize(
+                                        
JsonValueParserAdapter.createFor(jsonValue, parserFactory),
+                                        new 
JohnzonDeserializationContext(parser, builderFactoryInstance, jsonProvider),
+                                        targetType);
+                                }
+
+                                @Override
+                                public void writeJson(final Object instance, 
final MappingGenerator jsonbGenerator, JsonGenerator generator) {
+                                    // no-op, it's for factories only
+                                }
+                            };
                         }
                     } catch (final InstantiationException | 
IllegalAccessException e) {
                         throw new IllegalArgumentException(e);
@@ -607,8 +665,9 @@ public class JsonbAccessMode implements AccessMode, 
Closeable, Cleanable<Class<?
 
             final WriterConverters writerConverters = new 
WriterConverters(annotations, types);
             final JsonbProperty property = 
annotations.getAnnotation(JsonbProperty.class);
-            final JsonbNillable nillable = 
annotations.getClassOrPackageAnnotation(JsonbNillable.class);
-            final boolean isNillable = isNillable(property, nillable);
+            final JsonbNillable propertyNillable = 
annotations.getAnnotation(JsonbNillable.class);
+            final JsonbNillable classOrPackageNillable = 
annotations.getClassOrPackageAnnotation(JsonbNillable.class);
+            final boolean isNillable = isNillable(property, propertyNillable, 
classOrPackageNillable);
             final String key = property == null || property.value().isEmpty() 
? naming.translateName(entry.getKey()) : property.value();
             if (result.put(key, new Reader() {
                 @Override
@@ -725,8 +784,9 @@ public class JsonbAccessMode implements AccessMode, 
Closeable, Cleanable<Class<?
 
             final ReaderConverters converters = new 
ReaderConverters(initialWriter);
             final JsonbProperty property = 
initialWriter.getAnnotation(JsonbProperty.class);
-            final JsonbNillable nillable = 
initialWriter.getClassOrPackageAnnotation(JsonbNillable.class);
-            final boolean isNillable = isNillable(property, nillable);
+            final JsonbNillable propertyNillable = 
initialWriter.getAnnotation(JsonbNillable.class);
+            final JsonbNillable classOrPackageNillable = 
initialWriter.getClassOrPackageAnnotation(JsonbNillable.class);
+            final boolean isNillable = isNillable(property, propertyNillable, 
classOrPackageNillable);
             final String key = property == null || property.value().isEmpty() 
? naming.translateName(entry.getKey()) : property.value();
             if (result.put(key, new Writer() {
                 @Override
@@ -829,12 +889,14 @@ public class JsonbAccessMode implements AccessMode, 
Closeable, Cleanable<Class<?
                 });
     }
 
-    private boolean isNillable(final JsonbProperty property, final 
JsonbNillable nillable) {
-        if (property != null) {
+    private boolean isNillable(final JsonbProperty property, final 
JsonbNillable propertyNillable, final JsonbNillable classOrPackageNillable) {
+        if (propertyNillable != null) {
+            return propertyNillable.value();
+        } else if (property != null) {
             return property.nillable();
         }
-        if (nillable != null) {
-            return nillable.value();
+        if (classOrPackageNillable != null) {
+            return classOrPackageNillable.value();
         }
         return globalIsNillable;
     }
@@ -1121,8 +1183,7 @@ public class JsonbAccessMode implements AccessMode, 
Closeable, Cleanable<Class<?
                 final JsonbSerializer jsonbSerializer = instance.getValue();
                 writer = new ObjectConverter.Writer() {
                     @Override
-                    public void writeJson(final Object instance, final 
MappingGenerator jsonbGenerator) {
-                        final JsonGenerator generator = 
jsonbGenerator.getJsonGenerator();
+                    public void writeJson(final Object instance, final 
MappingGenerator jsonbGenerator, JsonGenerator generator) {
                         jsonbSerializer.serialize(instance, generator, new 
JohnzonSerializationContext(jsonbGenerator));
                     }
 
diff --git 
a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JohnzonConverterInJsonbTest.java
 
b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JohnzonConverterInJsonbTest.java
index 4ce2e13c..1b89cb25 100644
--- 
a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JohnzonConverterInJsonbTest.java
+++ 
b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JohnzonConverterInJsonbTest.java
@@ -27,6 +27,7 @@ import javax.json.JsonObject;
 import javax.json.JsonValue;
 import javax.json.bind.Jsonb;
 import javax.json.bind.spi.JsonbProvider;
+import javax.json.stream.JsonGenerator;
 
 import org.apache.johnzon.mapper.Converter;
 import org.apache.johnzon.mapper.JohnzonConverter;
@@ -88,8 +89,8 @@ public class JohnzonConverterInJsonbTest {
         private static final String TIMESTAMP_JSON_KEY = "timestamp";
 
         @Override
-        public void writeJson(TestDTO instance, MappingGenerator 
jsonbGenerator) {
-            jsonbGenerator.getJsonGenerator().write(TIMESTAMP_JSON_KEY, 
instance.instant.atZone(ZoneId.of("UTC")).toString());
+        public void writeJson(TestDTO instance, MappingGenerator 
jsonbGenerator, JsonGenerator generator) {
+            generator.write(TIMESTAMP_JSON_KEY, 
instance.instant.atZone(ZoneId.of("UTC")).toString());
         }
 
         @Override
diff --git 
a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/SerializerTest.java 
b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/SerializerTest.java
index 8a7d86b0..42f0c7c9 100644
--- a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/SerializerTest.java
+++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/SerializerTest.java
@@ -69,7 +69,7 @@ public class SerializerTest {
         nameHolder.name.detailName = new DetailName();
         nameHolder.name.detailName.name = "Another Test String";
         assertEquals(
-                "{\"detailName\":{\"name\":\"Another Test 
String\",\"detail\":true},\"name\":{\"name\":\"Test String\"}}",
+                "{\"name\":{\"detailName\":{\"name\":\"Another Test 
String\",\"detail\":true},\"name\":\"Test String\"}}",
                 jsonb.toJson(nameHolder));
 
     }
diff --git 
a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/SerializersRoundTripTest.java
 
b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/SerializersRoundTripTest.java
index 2a40a9a6..6a307b3c 100644
--- 
a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/SerializersRoundTripTest.java
+++ 
b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/SerializersRoundTripTest.java
@@ -564,7 +564,8 @@ public class SerializersRoundTripTest {
         original.color = Color.GREEN;*/
 
         try (final Jsonb jsonb = JsonbBuilder.create()) {
-            final Wrapper deserialized = 
jsonb.fromJson(jsonb.toJson(original), Wrapper.class);
+            final String json = jsonb.toJson(original);
+            final Wrapper deserialized = jsonb.fromJson(json, Wrapper.class);
             assertEquals(original, deserialized);
         }
     }
diff --git 
a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/serializer/SerialiseAsPrimitiveAnnotatedAtClassTest.java
 
b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/serializer/SerialiseAsPrimitiveAnnotatedAtClassTest.java
new file mode 100644
index 00000000..a74ce041
--- /dev/null
+++ 
b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/serializer/SerialiseAsPrimitiveAnnotatedAtClassTest.java
@@ -0,0 +1,129 @@
+/*
+ * 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.serializer;
+
+import java.lang.reflect.Type;
+
+import org.junit.Test;
+
+import javax.json.bind.Jsonb;
+import javax.json.bind.JsonbBuilder;
+import javax.json.bind.annotation.JsonbTypeDeserializer;
+import javax.json.bind.annotation.JsonbTypeSerializer;
+import javax.json.bind.serializer.DeserializationContext;
+import javax.json.bind.serializer.JsonbDeserializer;
+import javax.json.bind.serializer.JsonbSerializer;
+import javax.json.bind.serializer.SerializationContext;
+import javax.json.stream.JsonGenerator;
+import javax.json.stream.JsonParser;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * This test checks a JsonbSerialize/JsonbDeserialize roundtrip when using a 
primitive as placeholder
+ */
+public class SerialiseAsPrimitiveAnnotatedAtClassTest {
+
+
+    @JsonbTypeSerializer(ConstantJsonbSerialiser.class)
+    @JsonbTypeDeserializer(ConstantJsonbDeserializer.class)
+    public static class TestConstant {
+        public final static TestConstant VAL_1 = new TestConstant("A");
+        public final static TestConstant VAL_2 = new TestConstant("B");
+        public final static TestConstant VAL_3 = new TestConstant("C");
+
+        private final String code;
+
+        public TestConstant(String code) {
+            this.code = code;
+        }
+
+        public String getCode() {
+            return code;
+        }
+
+        public static TestConstant getByCode(String code) {
+            switch (code) {
+                case "A": return VAL_1;
+                case "B": return VAL_2;
+                case "C": return VAL_3;
+                default: return null;
+            }
+        }
+    }
+    
+    public static class ConstantUsage {
+        private int i;
+
+        private TestConstant testConstant;
+
+        public int getI() {
+            return i;
+        }
+
+        public void setI(int i) {
+            this.i = i;
+        }
+
+        public TestConstant getTestConstant() {
+            return testConstant;
+        }
+
+        public void setTestConstant(TestConstant testEnum) {
+            this.testConstant = testEnum;
+        }
+    }
+
+    public static class ConstantJsonbSerialiser implements 
JsonbSerializer<TestConstant> {
+        @Override
+        public void serialize(TestConstant val, JsonGenerator generator, 
SerializationContext ctx) {
+            if (val == null) {
+                ctx.serialize(null, generator);
+            } else {
+                ctx.serialize(val.getCode(), generator);
+            }
+        }
+    }
+
+    public static class ConstantJsonbDeserializer implements 
JsonbDeserializer<TestConstant> {
+
+    @Override
+    public TestConstant deserialize(JsonParser parser, DeserializationContext 
ctx, Type rtType) {
+        if (rtType instanceof TestConstant) {
+            final String key = parser.getString();
+            if (key != null) {
+                return TestConstant.getByCode(key);
+            }
+        }
+        
+        return null;
+    }
+}
+
+
+
+    @Test
+    public void testEnumJsonb() {
+        ConstantUsage enumVerwender = new ConstantUsage();
+        enumVerwender.setI(1);
+        enumVerwender.setTestConstant(TestConstant.VAL_2);
+        
+        Jsonb jsonb = JsonbBuilder.create();
+        final String json = jsonb.toJson(enumVerwender);
+        assertTrue(json.contains("\"testConstant\":\"B\""));
+    }
+
+}
diff --git 
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/DynamicMappingGenerator.java
 
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/DynamicMappingGenerator.java
index 163eadef..9c74b760 100644
--- 
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/DynamicMappingGenerator.java
+++ 
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/DynamicMappingGenerator.java
@@ -23,6 +23,7 @@ import javax.json.stream.JsonGenerator;
 import java.math.BigDecimal;
 import java.math.BigInteger;
 
+@Deprecated
 public class DynamicMappingGenerator implements MappingGenerator {
     private final MappingGenerator delegate;
     private final Runnable writeStart;
@@ -40,33 +41,16 @@ public class DynamicMappingGenerator implements 
MappingGenerator {
         this.writeEnd = writeEnd;
         this.keyName = keyName;
     }
-
-    protected JsonGenerator getRawJsonGenerator() {
-        return delegate.getJsonGenerator();
-    }
-
-    @Override
-    public JsonGenerator getJsonGenerator() {
-        return generator == null ? generator = new 
InObjectOrPrimitiveJsonGenerator(
-                getRawJsonGenerator(), writeStart, keyName, writeEnd) : 
generator;
-    }
+    
 
     @Override
     public MappingGenerator writeObject(final String key, final Object o, 
final JsonGenerator generator) {
-        return delegate.writeObject(key, o, ensureGenerator(generator));
+        return delegate.writeObject(key, o, generator);
     }
 
     @Override
     public MappingGenerator writeObject(final Object o, final JsonGenerator 
generator) {
-        return delegate.writeObject(o, ensureGenerator(generator));
-    }
-
-    private JsonGenerator ensureGenerator(final JsonGenerator generator) {
-        if (this.generator != null && this.generator != generator && 
this.generator.delegate != generator) {
-            this.generator = null;
-            reset();
-        }
-        return getJsonGenerator(); // ensure we wrap it
+        return delegate.writeObject(o, generator);
     }
 
     protected void reset() {
@@ -593,24 +577,5 @@ public class DynamicMappingGenerator implements 
MappingGenerator {
             super(delegate, NOOP, NOOP, keyName);
             this.rawGenerator = generator;
         }
-
-        @Override
-        protected JsonGenerator getRawJsonGenerator() {
-            return rawGenerator;
-        }
-
-        @Override
-        public JsonGenerator getJsonGenerator() {
-            if (skippingGenerator == null) {
-                skippingGenerator = new 
SkipLastWriteEndGenerator(super.getJsonGenerator());
-            }
-            return skippingGenerator;
-        }
-
-        @Override
-        protected void reset() {
-            super.reset();
-            skippingGenerator = null;
-        }
     }
 }
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java 
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
index 022eaee0..aaded0ed 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
@@ -203,7 +203,7 @@ public class Mapper implements Closeable {
 
     private void writeObject(final Object object, final JsonGenerator 
generator, final Collection<String> ignored,
                              final JsonPointerTracker tracker) {
-        final MappingGeneratorImpl mappingGenerator = new 
MappingGeneratorImpl(config, generator, mappings);
+        final MappingGeneratorImpl mappingGenerator = new 
MappingGeneratorImpl(config, mappings);
         mappingGenerator.doWriteObject(object, generator, true, ignored, 
tracker);
     }
 
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 813e61c6..6f003bdb 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
@@ -25,6 +25,7 @@ import org.apache.johnzon.mapper.map.LazyConverterMap;
 
 import javax.json.Json;
 import javax.json.JsonValue;
+import javax.json.stream.JsonGenerator;
 import java.lang.reflect.Type;
 import java.nio.charset.Charset;
 import java.util.Collection;
@@ -48,7 +49,7 @@ public /* DON'T MAKE IT HIDDEN */ class MapperConfig 
implements Cloneable {
 
     private static final ObjectConverter.Codec NO_CONVERTER = new 
ObjectConverter.Codec() {
         @Override
-        public void writeJson(Object instance, MappingGenerator 
jsonbGenerator) {
+        public void writeJson(Object instance, MappingGenerator 
jsonbGenerator, JsonGenerator generator) {
             // just a dummy
         }
 
diff --git 
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGenerator.java 
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGenerator.java
index 3e52812b..9dd3503b 100644
--- 
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGenerator.java
+++ 
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGenerator.java
@@ -24,16 +24,11 @@ import javax.json.stream.JsonGenerator;
  * Handles writing Json for Objects.
  * Internally it uses a {@link JsonGenerator} to write JSON
  *
- * To write JSON-P structure elements you can use the {@link 
#getJsonGenerator()} method.
+ * To write JSON-P structure elements you can use the {@link JsonGenerator}.
  *
  */
 public interface MappingGenerator {
 
-    /**
-     * @return the {@link JsonGenerator} used internally to write the JSON 
output.
-     */
-    JsonGenerator getJsonGenerator();
-
     /**
      * Write the given Object o into the current JSON layer.
      * This will <em>not</em> open a new json layer ('{', '}')
diff --git 
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGeneratorImpl.java
 
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGeneratorImpl.java
index f7347997..14df9b30 100644
--- 
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGeneratorImpl.java
+++ 
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGeneratorImpl.java
@@ -23,6 +23,7 @@ import static java.util.Collections.emptyMap;
 import static java.util.stream.Collectors.toList;
 
 import org.apache.johnzon.mapper.internal.JsonPointerTracker;
+import org.apache.johnzon.mapper.jsonp.DeferredStartJsonGenerator;
 import org.apache.johnzon.mapper.util.ArrayUtil;
 
 import javax.json.JsonValue;
@@ -44,21 +45,14 @@ import java.util.stream.StreamSupport;
 
 public class MappingGeneratorImpl implements MappingGenerator {
     private final MapperConfig config;
-    private final JsonGenerator generator;
     private final Mappings mappings;
     private Map<Object, String> jsonPointers;
 
-    MappingGeneratorImpl(MapperConfig config, JsonGenerator jsonGenerator, 
final Mappings mappings) {
+    MappingGeneratorImpl(MapperConfig config, final Mappings mappings) {
         this.config = config;
-        this.generator = jsonGenerator;
         this.mappings = mappings;
     }
 
-    @Override
-    public JsonGenerator getJsonGenerator() {
-        return generator;
-    }
-
     @Override
     public MappingGenerator writeObject(final String key, final Object object, 
final JsonGenerator generator) {
         if (object == null) {
@@ -86,8 +80,9 @@ public class MappingGeneratorImpl implements MappingGenerator 
{
                 } else {
                     final ObjectConverter.Writer objectConverter = 
config.findObjectConverterWriter(objectClass);
                     if (objectConverter != null) {
-                        writeWithObjectConverter(new 
DynamicMappingGenerator(this,
-                                generator::writeStartObject, 
generator::writeEnd, null), objectConverter, object);
+                        DeferredStartJsonGenerator deferredStartJsonGenerator 
= new DeferredStartJsonGenerator(generator, key);
+                        objectConverter.writeJson(object, this, 
deferredStartJsonGenerator);
+                        deferredStartJsonGenerator.writeEnd();
                     } else {
                         writeValue(objectClass, false, false, false, false, 
false, null, key, object,
                                 null, emptyList(), isDedup() ? 
JsonPointerTracker.ROOT : null, generator);
@@ -124,7 +119,7 @@ public class MappingGeneratorImpl implements 
MappingGenerator {
                 if (writeBody) {
                     generator.writeStartObject();
                 }
-                writeMapBody((Map<?, ?>) object, null);
+                writeMapBody((Map<?, ?>) object, null, generator);
                 if (writeBody) {
                     generator.writeEnd();
                 }
@@ -145,12 +140,12 @@ public class MappingGeneratorImpl implements 
MappingGenerator {
 
             if (objectClass.isArray()) {
                 final Adapter adapter = config.findAdapter(objectClass);
-                writeArray(objectClass, adapter, null, object, 
ignoredProperties, jsonPointer);
+                writeArray(objectClass, adapter, null, object, 
ignoredProperties, jsonPointer, generator);
                 return;
             }
 
             if (object instanceof Iterable) {
-                doWriteIterable((Iterable) object, ignoredProperties, 
jsonPointer);
+                doWriteIterable((Iterable) object, ignoredProperties, 
jsonPointer, generator);
                 return;
             }
 
@@ -172,10 +167,11 @@ public class MappingGeneratorImpl implements 
MappingGenerator {
             ObjectConverter.Writer objectConverter = 
config.findObjectConverterWriter(objectClass);
             if (writeBody && objectConverter != null) {
                 if (!writeBody) {
-                    objectConverter.writeJson(object, this);
+                    objectConverter.writeJson(object, this, generator);
                 } else {
-                    writeWithObjectConverter(new DynamicMappingGenerator(this,
-                            generator::writeStartObject, generator::writeEnd, 
null), objectConverter, object);
+                    DeferredStartJsonGenerator deferredGenerator = new 
DeferredStartJsonGenerator(generator, null);
+                    objectConverter.writeJson(object, this, deferredGenerator);
+                    deferredGenerator.writeEnd();
                 }
             } else {
                 if (classMapping == null) { // will be created anyway now so 
force it and if it has an adapter respect it
@@ -187,7 +183,7 @@ public class MappingGeneratorImpl implements 
MappingGenerator {
                     }
                 }
                 if (writeBody) {
-                    generator.writeStartObject();
+                    generator = new DeferredStartJsonGenerator(generator, 
null);
                 }
                 final boolean writeEnd;
                 if (config.getSerializationPredicate() != null && 
config.getSerializationPredicate().test(objectClass)) {
@@ -205,8 +201,10 @@ public class MappingGeneratorImpl implements 
MappingGenerator {
         }
     }
 
-    private JsonGenerator writeMapBody(final Map<?, ?> object, final Adapter 
itemConverter) throws InvocationTargetException, IllegalAccessException {
-        for (final Map.Entry<?, ?> entry : ((Map<?, ?>) object).entrySet()) {
+    private JsonGenerator writeMapBody(final Map<?, ?> object, final Adapter 
itemConverter,
+                                       final JsonGenerator generator)
+            throws InvocationTargetException, IllegalAccessException {
+        for (final Map.Entry<?, ?> entry : object.entrySet()) {
             final Object value = entry.getValue();
             final Object key = entry.getKey();
 
@@ -366,7 +364,9 @@ public class MappingGeneratorImpl implements 
MappingGenerator {
         }
 
         if (classMapping.writer != null) {
-            writeWithObjectConverter(new 
DynamicMappingGenerator.SkipEnclosingWriteEnd(this, null, generator), 
classMapping.writer, object);
+            DeferredStartJsonGenerator deferredStartJsonGenerator = new 
DeferredStartJsonGenerator(generator, null);
+            classMapping.writer.writeJson(object, this, 
deferredStartJsonGenerator);
+            deferredStartJsonGenerator.writeEnd();
             return false;
         }
         if (classMapping.adapter != null) {
@@ -426,7 +426,7 @@ public class MappingGeneratorImpl implements 
MappingGenerator {
         if (classMapping.anyGetter != null) {
             final Map<String, Object> any = 
Map.class.cast(classMapping.anyGetter.reader.read(object));
             if (any != null) {
-                writeMapBody(any, null);
+                writeMapBody(any, null, generator);
             }
         }
 
@@ -449,23 +449,23 @@ public class MappingGeneratorImpl implements 
MappingGenerator {
             return;
         }
         if ((!dynamic && array) || (dynamic && type.isArray())) {
-            writeArray(type, itemConverter, key, value, ignoredProperties, 
jsonPointer);
+            writeArray(type, itemConverter, key, value, ignoredProperties, 
jsonPointer, generator);
         } else if ((!dynamic && collection) || (dynamic && 
Iterable.class.isAssignableFrom(type))) {
             writeIterator(itemConverter, key, objectConverter, 
ignoredProperties, jsonPointer, generator,
                     Iterable.class.cast(value).iterator(), value);
         } else if ((!dynamic && map) || (dynamic && 
Map.class.isAssignableFrom(type))) {
-            generator.writeStartObject(key);
+            DeferredStartJsonGenerator deferredStartJsonGenerator = new 
DeferredStartJsonGenerator(generator, key);
             if (objectConverter != null) {
-                writeWithObjectConverter(new DynamicMappingGenerator(this,
-                        () -> this.generator.writeStartObject(key), 
this.generator::writeEnd, key), objectConverter, value);
+                objectConverter.writeJson(value, this, 
deferredStartJsonGenerator);
             } else {
-                writeMapBody((Map<?, ?>) value, itemConverter);
+                writeMapBody((Map<?, ?>) value, itemConverter, 
deferredStartJsonGenerator);
             }
-            generator.writeEnd();
+            deferredStartJsonGenerator.writeEnd();
         } else if ((!dynamic && primitive) || (dynamic && 
Mappings.isPrimitive(type))) {
             if (objectConverter != null) {
-                writeWithObjectConverter(new DynamicMappingGenerator(this,
-                        () -> this.generator.writeStartObject(key), 
this.generator::writeEnd, key), objectConverter, value);
+                DeferredStartJsonGenerator deferredStartJsonGenerator = new 
DeferredStartJsonGenerator(generator, key);
+                objectConverter.writeJson(value, this, 
deferredStartJsonGenerator);
+                deferredStartJsonGenerator.writeEnd();
             } else {
                 writePrimitives(key, type, value, generator);
             }
@@ -475,8 +475,9 @@ public class MappingGeneratorImpl implements 
MappingGenerator {
         } else if (Iterator.class.isAssignableFrom(type)) {
             if (objectConverter != null) {
                 generator.writeStartObject(key);
+                //X TODO 2 writeStartObject? sounds fishy...
                 writeWithObjectConverter(new DynamicMappingGenerator(this,
-                        () -> this.generator.writeStartObject(key), 
this.generator::writeEnd, key), objectConverter, value);
+                        () -> generator.writeStartObject(key), 
generator::writeEnd, key), objectConverter, value, generator);
                 generator.writeEnd();
             } else {
                 writeIterator(itemConverter, key, objectConverter, 
ignoredProperties, jsonPointer, generator,
@@ -484,8 +485,9 @@ public class MappingGeneratorImpl implements 
MappingGenerator {
             }
         } else {
             if (objectConverter != null) {
-                writeWithObjectConverter(new DynamicMappingGenerator(this,
-                        () -> this.generator.writeStartObject(key), 
this.generator::writeEnd, key), objectConverter, value);
+                DeferredStartJsonGenerator deferredStartJsonGenerator = new 
DeferredStartJsonGenerator(generator, key);
+                objectConverter.writeJson(value, this, 
deferredStartJsonGenerator);
+                deferredStartJsonGenerator.writeEnd();
                 return;
             }
 
@@ -504,26 +506,27 @@ public class MappingGeneratorImpl implements 
MappingGenerator {
                 }
 
                 if (objectConverterToUse != null) {
-                    writeWithObjectConverter(new DynamicMappingGenerator(this,
-                            () -> this.generator.writeStartObject(key), 
this.generator::writeEnd, key), objectConverterToUse, value);
+                    DeferredStartJsonGenerator deferredStartJsonGenerator = 
new DeferredStartJsonGenerator(generator, key);
+                    objectConverterToUse.writeJson(value, this, 
deferredStartJsonGenerator);
+                    deferredStartJsonGenerator.writeEnd();
                     return;
                 }
             }
             if (writePrimitives(key, type, value, generator)) {
                 return;
             }
-            generator.writeStartObject(key);
-            if (doWriteObjectBody(value, ignoredProperties, jsonPointer, 
generator)) {
-                generator.writeEnd();
+            DeferredStartJsonGenerator deferredStartGenerator = new 
DeferredStartJsonGenerator(generator, key);
+            if (doWriteObjectBody(value, ignoredProperties, jsonPointer, 
deferredStartGenerator)) {
+                deferredStartGenerator.writeEnd();
             }
         }
     }
 
-    private void writeWithObjectConverter(final DynamicMappingGenerator 
generator,
+    @Deprecated
+    private void writeWithObjectConverter(final DynamicMappingGenerator 
dynamicMappingGenerator,
                                           final ObjectConverter.Writer 
objectConverter,
-                                          final Object value) {
-        final DynamicMappingGenerator dynamicMappingGenerator = generator;
-        objectConverter.writeJson(value, dynamicMappingGenerator);
+                                          final Object value, JsonGenerator 
generator) {
+        objectConverter.writeJson(value, dynamicMappingGenerator, generator);
         dynamicMappingGenerator.flushIfNeeded();
     }
 
@@ -539,8 +542,9 @@ public class MappingGeneratorImpl implements 
MappingGenerator {
                     List.class.cast(originalValue) :
                     
StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 
Spliterator.IMMUTABLE), false)
                         .collect(toList());
-            objectConverter.writeJson(list, new DynamicMappingGenerator(
-                    this, generator::writeStartArray, generator::writeEnd, 
key));
+            DeferredStartJsonGenerator deferredStartJsonGenerator = new 
DeferredStartJsonGenerator(generator, key, true);
+            objectConverter.writeJson(list, this, deferredStartJsonGenerator);
+            deferredStartJsonGenerator.writeEnd();
             return;
         }
 
@@ -559,11 +563,12 @@ public class MappingGeneratorImpl implements 
MappingGenerator {
                 }
 
                 if (objectConverterToUse != null) {
-                    writeWithObjectConverter(new DynamicMappingGenerator(this,
-                            generator::writeStartObject, generator::writeEnd, 
null), objectConverterToUse, o);
+                    DeferredStartJsonGenerator deferredStartJsonGenerator = 
new DeferredStartJsonGenerator(generator, null);
+                    objectConverterToUse.writeJson(o, this, 
deferredStartJsonGenerator);
+                    deferredStartJsonGenerator.writeEnd();
                 } else {
                     writeItem(itemConverter != null ? itemConverter.from(o) : 
o, ignoredProperties,
-                            isDedup() ? new JsonPointerTracker(jsonPointer, i) 
: null);
+                            isDedup() ? new JsonPointerTracker(jsonPointer, i) 
: null, generator);
                 }
             }
             i++;
@@ -573,9 +578,12 @@ public class MappingGeneratorImpl implements 
MappingGenerator {
 
     /**
      * Write a JSON Array with a given Array Value, like byte[], int[], 
Person[] etc.
-     * @param key either the attribute key or {@code null} if the array should 
be rendered without key
+     *
+     * @param key       either the attribute key or {@code null} if the array 
should be rendered without key
+     * @param generator
      */
-    private void writeArray(Class<?> type, Adapter itemConverter, String key, 
Object arrayValue, Collection<String> ignoredProperties, JsonPointerTracker 
jsonPointer) {
+    private void writeArray(Class<?> type, Adapter itemConverter, String key, 
Object arrayValue, Collection<String> ignoredProperties, JsonPointerTracker 
jsonPointer,
+                            final JsonGenerator generator) {
         final int length = ArrayUtil.getArrayLength(arrayValue);
         if (length == 0 && config.isSkipEmptyArray()) {
             return;
@@ -668,7 +676,7 @@ public class MappingGeneratorImpl implements 
MappingGenerator {
             for (int i = 0; i < length; i++) {
                 final Object o = oArrayValue[i];
                 writeItem(itemConverter != null ? itemConverter.from(o) : o, 
ignoredProperties,
-                        isDedup() ? new JsonPointerTracker(jsonPointer, i) : 
null);
+                        isDedup() ? new JsonPointerTracker(jsonPointer, i) : 
null, generator);
             }
         } else {
             // must be object arrays
@@ -683,7 +691,7 @@ public class MappingGeneratorImpl implements 
MappingGenerator {
                     generator.write((JsonValue) o);
                 } else {
                     writeItem(itemConverter != null ? itemConverter.from(o) : 
o, ignoredProperties,
-                            isDedup() ? new JsonPointerTracker(jsonPointer, i) 
: null);
+                            isDedup() ? new JsonPointerTracker(jsonPointer, i) 
: null, generator);
                 }
             }
         }
@@ -691,16 +699,17 @@ public class MappingGeneratorImpl implements 
MappingGenerator {
     }
 
 
-    private void writeItem(final Object o, final Collection<String> 
ignoredProperties, JsonPointerTracker jsonPointer) {
+    private void writeItem(final Object o, final Collection<String> 
ignoredProperties, JsonPointerTracker jsonPointer,
+                           final JsonGenerator generator) {
         if (o == null) {
             generator.writeNull();
         } else if (!writePrimitives(o, generator)) {
             if (Collection.class.isInstance(o)) {
-                doWriteIterable(Collection.class.cast(o), ignoredProperties, 
jsonPointer);
+                doWriteIterable(Collection.class.cast(o), ignoredProperties, 
jsonPointer, generator);
             } else if (o.getClass().isArray()) {
                 final int length = ArrayUtil.getArrayLength(o);
                 if (length > 0 || !config.isSkipEmptyArray()) {
-                    writeArray(o.getClass(), null, null, o, ignoredProperties, 
jsonPointer);
+                    writeArray(o.getClass(), null, null, o, ignoredProperties, 
jsonPointer, generator);
                 }
             } else {
                 String valJsonPointer = jsonPointers == null ? null : 
jsonPointers.get(o);
@@ -714,7 +723,8 @@ public class MappingGeneratorImpl implements 
MappingGenerator {
         }
     }
 
-    private <T> void doWriteIterable(final Iterable<T> object, final 
Collection<String> ignoredProperties, JsonPointerTracker jsonPointer) {
+    private <T> void doWriteIterable(final Iterable<T> object, final 
Collection<String> ignoredProperties, JsonPointerTracker jsonPointer,
+                                     final JsonGenerator generator) {
         if (object == null) {
             generator.writeStartArray().writeEnd();
         } else {
@@ -727,7 +737,7 @@ public class MappingGeneratorImpl implements 
MappingGenerator {
                     if (t == null) {
                         generator.writeNull();
                     } else {
-                        writeItem(t, ignoredProperties, isDedup() ? new 
JsonPointerTracker(jsonPointer, i) : null);
+                        writeItem(t, ignoredProperties, isDedup() ? new 
JsonPointerTracker(jsonPointer, i) : null, generator);
                     }
                 }
                 i++;
diff --git 
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/ObjectConverter.java 
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/ObjectConverter.java
index 673585d1..e2f36c12 100644
--- 
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/ObjectConverter.java
+++ 
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/ObjectConverter.java
@@ -19,6 +19,7 @@
 package org.apache.johnzon.mapper;
 
 import javax.json.JsonValue;
+import javax.json.stream.JsonGenerator;
 
 import java.lang.reflect.Type;
 
@@ -36,7 +37,7 @@ public final class ObjectConverter {
     }
 
     public interface Writer<T> extends MapperConverter {
-        void writeJson(T instance, MappingGenerator jsonbGenerator);
+        void writeJson(T instance, MappingGenerator jsonbGenerator, 
JsonGenerator generator);
 
         // returns true if it is for containers - if any - and not each 
container item (ex: list)
         default boolean isGlobal() {
diff --git 
a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/jsonp/DeferredStartJsonGenerator.java
 
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/jsonp/DeferredStartJsonGenerator.java
new file mode 100644
index 00000000..99c717cc
--- /dev/null
+++ 
b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/jsonp/DeferredStartJsonGenerator.java
@@ -0,0 +1,354 @@
+/*
+ * 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.jsonp;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import javax.json.JsonValue;
+import javax.json.stream.JsonGenerator;
+
+
+/**
+ * This JsonGenerator will not automatically write a startObject '{' 
character, but only if needed.
+ *
+ * The {@link #writeEnd()} method will only write a closing '}' if a start has 
been written before.
+ * 
+ * This class must only be used in cases where you would call {@code 
jsonGenerator.startObject(key)} !
+ */
+public class DeferredStartJsonGenerator implements JsonGenerator {
+
+    private final JsonGenerator delegate;
+    private final String key;
+    private final boolean array;
+
+    private boolean started = false;
+    private boolean empty = true;
+
+    // this is needed to make sure we don't close more layers than we did open.
+    private int depth = 0;
+
+    /**
+     * Deferred start for Objects
+     *
+     * @see #DeferredStartJsonGenerator(JsonGenerator, String, boolean)
+     */
+    public DeferredStartJsonGenerator(JsonGenerator delegate, String key) {
+        this(delegate, key, false);
+    }
+
+    /**
+     * JsonGenerator which only writes a start character if an embedded json 
structure is later written.
+     *
+     * @param delegate JsonGenerator which really writes
+     * @param key for the startObject, or {@code null} if no key should be used
+     * @param array if {@code true} we will use a start with a '[', otherwise 
with an object start '{'
+     */
+    public DeferredStartJsonGenerator(JsonGenerator delegate, String key, 
boolean array) {
+        this.delegate = delegate;
+        this.key = key;
+        this.array = array;
+    }
+
+    private void ensureStart() {
+        if (!started) {
+            if (array) {
+                if (key != null) {
+                    delegate.writeStartArray(key);
+                } else {
+                    delegate.writeStartArray();
+                }
+            } else {
+                if (key != null) {
+                    delegate.writeStartObject(key);
+                } else {
+                    delegate.writeStartObject();
+                }
+            }
+            started = true;
+            depth++;
+        }
+    }
+
+    @Override
+    public void close() {
+        writeEnd();
+        delegate.close();
+    }
+
+    @Override
+    public void flush() {
+        delegate.flush();
+    }
+
+    @Override
+    public JsonGenerator write(String name, BigDecimal value) {
+        ensureStart();
+        empty = false;
+        delegate.write(name, value);
+        empty = false;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, BigInteger value) {
+        ensureStart();
+        delegate.write(name, value);
+        empty = false;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, boolean value) {
+        ensureStart();
+        delegate.write(name, value);
+        empty = false;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, double value) {
+        ensureStart();
+        delegate.write(name, value);
+        empty = false;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, int value) {
+        ensureStart();
+        delegate.write(name, value);
+        empty = false;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, JsonValue value) {
+        ensureStart();
+        delegate.write(name, value);
+        empty = false;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, long value) {
+        ensureStart();
+        delegate.write(name, value);
+        empty = false;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, String value) {
+        ensureStart();
+        delegate.write(name, value);
+        empty = false;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(BigDecimal value) {
+        if (!started && key != null) {
+            // means we write a value instead of an object
+            delegate.write(key, value);
+        } else {
+            delegate.write(value);
+        }
+        empty = false;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(BigInteger value) {
+        if (!started && key != null) {
+            // means we write a value instead of an object
+            delegate.write(key, value);
+        } else {
+            delegate.write(value);
+        }
+        empty = false;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(boolean value) {
+        if (!started && key != null) {
+            // means we write a value instead of an object
+            delegate.write(key, value);
+        } else {
+            delegate.write(value);
+        }
+        empty = false;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(double value) {
+        if (!started && key != null) {
+            // means we write a value instead of an object
+            delegate.write(key, value);
+        } else {
+            delegate.write(value);
+        }
+        empty = false;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(int value) {
+        if (!started && key != null) {
+            // means we write a value instead of an object
+            delegate.write(key, value);
+        } else {
+            delegate.write(value);
+        }
+        empty = false;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(JsonValue value) {
+        if (!started && key != null) {
+            // means we write a value instead of an object
+            delegate.write(key, value);
+        } else {
+            delegate.write(value);
+        }
+        empty = false;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(long value) {
+        if (!started && key != null) {
+            // means we write a value instead of an object
+            delegate.write(key, value);
+        } else {
+            delegate.write(value);
+        }
+        empty = false;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String value) {
+        if (!started && key != null) {
+            // means we write a value instead of an object
+            delegate.write(key, value);
+        } else {
+            delegate.write(value);
+        }
+        empty = false;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeEnd() {
+        if (empty) {
+            if (key != null) {
+                delegate.writeStartObject(key);
+            } else {
+                delegate.writeStartObject();
+            }
+            started = true;
+            depth++;
+        }
+        if (started && depth > 0) {
+            delegate.writeEnd();
+            depth--;
+        }
+
+
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeKey(String name) {
+        delegate.writeKey(name);
+        empty = false;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeNull() {
+        if (!started && key != null) {
+            // means we write a value instead of an object
+            delegate.writeNull(key);
+        } else {
+            delegate.writeNull();
+        }
+        empty = false;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeNull(String name) {
+        ensureStart();
+        delegate.writeNull(name);
+        empty = false;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartArray() {
+        // safeguard if Converters, Serializers ets do a manual startArray()
+        if (key != null && !started) {
+            delegate.writeStartArray(key);
+        } else {
+            delegate.writeStartArray();
+        }
+        started = true;
+        empty = false;
+        depth++;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartArray(String name) {
+        ensureStart();
+        delegate.writeStartArray(name);
+        started = true;
+        empty = false;
+        depth++;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartObject() {
+        // safeguard if Converters, Serializers ets do a manual startObject()
+        if (key != null && !started) {
+            delegate.writeStartObject(key);
+        } else {
+            delegate.writeStartObject();
+        }
+
+        started = true;
+        empty = false;
+        depth++;
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartObject(String name) {
+        ensureStart();
+        started = true;
+        delegate.writeStartObject(name);
+        empty = false;
+        depth++;
+        return this;
+    }
+}
diff --git 
a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/MapperConfigTest.java 
b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/MapperConfigTest.java
index 4307d11d..a5825d32 100644
--- 
a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/MapperConfigTest.java
+++ 
b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/MapperConfigTest.java
@@ -27,6 +27,7 @@ import org.junit.Assert;
 import org.junit.Test;
 
 import javax.json.JsonValue;
+import javax.json.stream.JsonGenerator;
 
 import java.lang.reflect.Type;
 import java.nio.charset.StandardCharsets;
@@ -189,7 +190,7 @@ public class MapperConfigTest {
 
     private static class TheConverter<T> implements ObjectConverter.Codec<T>{
         @Override
-        public void writeJson(T instance, MappingGenerator jsonbGenerator) {
+        public void writeJson(T instance, MappingGenerator jsonbGenerator, 
JsonGenerator generator) {
             // dummy
         }
 
@@ -202,7 +203,7 @@ public class MapperConfigTest {
 
     private static abstract class TheAbstractConverter<T extends TheInterface> 
implements ObjectConverter.Codec<T> {
         @Override
-        public void writeJson(T instance, MappingGenerator jsonbGenerator) {
+        public void writeJson(T instance, MappingGenerator jsonbGenerator, 
JsonGenerator generator) {
             // dummy
         }
 
diff --git 
a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectConverterWithAnnotationTest.java
 
b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectConverterWithAnnotationTest.java
index 9c79c7e9..c6cf46a6 100644
--- 
a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectConverterWithAnnotationTest.java
+++ 
b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectConverterWithAnnotationTest.java
@@ -24,6 +24,7 @@ import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
 import javax.json.JsonValue;
+import javax.json.stream.JsonGenerator;
 
 import java.beans.ConstructorProperties;
 import java.lang.reflect.Type;
@@ -388,11 +389,11 @@ public class ObjectConverterWithAnnotationTest {
 
 
         @Override
-        public void writeJson(Bike instance, MappingGenerator jsonbGenerator) {
-            jsonbGenerator.getJsonGenerator().write(MANUFACTURER_ID, 
MANUFACTURERS.indexOf(instance.getManufacturer()));
+        public void writeJson(Bike instance, MappingGenerator jsonbGenerator, 
JsonGenerator generator) {
+            generator.write(MANUFACTURER_ID, 
MANUFACTURERS.indexOf(instance.getManufacturer()));
 
             // i know you should never use this in production but its good for 
our sample ;)
-            jsonbGenerator.getJsonGenerator().write(TYPE_INDEX, 
instance.getType().ordinal());
+            generator.write(TYPE_INDEX, instance.getType().ordinal());
         }
 
         @Override
diff --git 
a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java 
b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java
index 785cdbf6..a08c81ab 100644
--- a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java
+++ b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/ObjectTypeTest.java
@@ -25,6 +25,7 @@ import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
 import javax.json.JsonValue;
+import javax.json.stream.JsonGenerator;
 
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
@@ -200,9 +201,9 @@ public class ObjectTypeTest {
 
     public static class TestWithTypeConverter implements 
ObjectConverter.Codec<Dog> {
         @Override
-        public void writeJson(Dog instance, MappingGenerator mappingGenerator) 
{
-            mappingGenerator.getJsonGenerator().write("//javaType", 
instance.getClass().getName());
-            mappingGenerator.writeObject(instance, 
mappingGenerator.getJsonGenerator());
+        public void writeJson(Dog instance, MappingGenerator mappingGenerator, 
JsonGenerator generator) {
+            generator.write("//javaType", instance.getClass().getName());
+            mappingGenerator.writeObject(instance, generator);
         }
 
         @Override
@@ -420,8 +421,8 @@ public class ObjectTypeTest {
         }
 
         @Override
-        public void writeJson(Poodle instance, MappingGenerator 
jsonbGenerator) {
-            jsonbGenerator.getJsonGenerator().write("poodleName", 
instance.getName());
+        public void writeJson(Poodle instance, MappingGenerator 
jsonbGenerator, JsonGenerator generator) {
+            generator.write("poodleName", instance.getName());
         }
 
         @Override

Reply via email to