This is an automated email from the ASF dual-hosted git repository. rmannibucau pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/johnzon.git
The following commit(s) were added to refs/heads/master by this push: new 1e7c5fd [JOHNZON-335] workaround for nested JsonbSerializers handling and invalid JSON 1e7c5fd is described below commit 1e7c5fda86789f78a98b21dfecacb826854819e0 Author: Romain Manni-Bucau <rmannibu...@gmail.com> AuthorDate: Thu Feb 4 09:46:20 2021 +0100 [JOHNZON-335] workaround for nested JsonbSerializers handling and invalid JSON --- .../org/apache/johnzon/core/JsonGeneratorImpl.java | 2 +- .../org/apache/johnzon/jsonb/SerializerTest.java | 33 +++ .../johnzon/mapper/DynamicMappingGenerator.java | 232 ++++++++++++++++++++- .../johnzon/mapper/MappingGeneratorImpl.java | 6 +- 4 files changed, 265 insertions(+), 8 deletions(-) diff --git a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonGeneratorImpl.java b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonGeneratorImpl.java index 852aa2e..7520243 100644 --- a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonGeneratorImpl.java +++ b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonGeneratorImpl.java @@ -463,7 +463,7 @@ class JsonGeneratorImpl implements JsonGenerator, JsonChars, Serializable { JsonGenerationException ex = null; final GeneratorState state = currentState(); if (state != GeneratorState.END && state != GeneratorState.ROOT_VALUE) { - ex = new JsonGenerationException("Invalid json"); + ex = new JsonGenerationException("Invalid json, state=" + state); } try { if (ex == null) { 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 fe658d5..8a7d86b 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 @@ -55,6 +55,12 @@ public class SerializerTest { public final JsonbRule jsonb = new JsonbRule() .withPropertyOrderStrategy(PropertyOrderStrategy.LEXICOGRAPHICAL); + @Test // https://issues.apache.org/jira/browse/JOHNZON-335 + public void testNestedSerializer() { + final String s = jsonb.toJson(new OuterTestModel()); + assertEquals("{\"foo\":\"generated in outer serializer\",\"inner\":{\"bar\":\"generated in inner serializer\"}}", s); + } + @Test public void passthroughSerializer() { final NameHolder nameHolder = new NameHolder(); @@ -770,4 +776,31 @@ public class SerializerTest { this.student = student; } } + + @JsonbTypeSerializer(OuterTestSerializer.class) + public static class OuterTestModel { + } + + @JsonbTypeSerializer(InnerTestSerializer.class) + public static class InnerTestModel { + } + + public static class OuterTestSerializer implements JsonbSerializer<OuterTestModel> { + @Override + public void serialize(final OuterTestModel obj, final JsonGenerator generator, final SerializationContext ctx) { + generator.writeStartObject(); + generator.write("foo", "generated in outer serializer"); + ctx.serialize("inner", new InnerTestModel(), generator); + generator.writeEnd(); + } + } + + public static class InnerTestSerializer implements JsonbSerializer<InnerTestModel> { + @Override + public void serialize(final InnerTestModel obj, final JsonGenerator generator, final SerializationContext ctx) { + generator.writeStartObject(); + generator.write("bar", "generated in inner serializer"); + generator.writeEnd(); + } + } } 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 35c6c2e..e1f6f0e 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 @@ -18,11 +18,10 @@ */ package org.apache.johnzon.mapper; -import java.math.BigDecimal; -import java.math.BigInteger; - import javax.json.JsonValue; import javax.json.stream.JsonGenerator; +import java.math.BigDecimal; +import java.math.BigInteger; public class DynamicMappingGenerator implements MappingGenerator { private final MappingGenerator delegate; @@ -30,7 +29,7 @@ public class DynamicMappingGenerator implements MappingGenerator { private final Runnable writeEnd; private final String keyName; - private InObjectOrPrimitiveJsonGenerator generator; + protected InObjectOrPrimitiveJsonGenerator generator; public DynamicMappingGenerator(final MappingGenerator delegate, final Runnable writeStart, @@ -42,10 +41,14 @@ public class DynamicMappingGenerator implements MappingGenerator { this.keyName = keyName; } + protected JsonGenerator getRawJsonGenerator() { + return delegate.getJsonGenerator(); + } + @Override public JsonGenerator getJsonGenerator() { return generator == null ? generator = new InObjectOrPrimitiveJsonGenerator( - delegate.getJsonGenerator(), writeStart, keyName) : generator; + getRawJsonGenerator(), writeStart, keyName) : generator; } @Override @@ -61,10 +64,15 @@ public class DynamicMappingGenerator implements MappingGenerator { 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 } + protected void reset() { + // no-op + } + public void flushIfNeeded() { if (this.generator.state == WritingState.WROTE_START) { writeEnd.run(); @@ -342,4 +350,218 @@ public class DynamicMappingGenerator implements MappingGenerator { delegate.flush(); } } + + private static abstract class DelegatingGenerator implements JsonGenerator { + protected final JsonGenerator delegate; + + protected DelegatingGenerator(final JsonGenerator generator) { + this.delegate = generator; + } + + @Override + public JsonGenerator writeKey(final String name) { + delegate.writeKey(name); + return this; + } + + @Override + public JsonGenerator write(final String name, final JsonValue value) { + delegate.write(name, value); + return this; + } + + @Override + public JsonGenerator write(final String name, final String value) { + delegate.write(name, value); + return this; + } + + @Override + public JsonGenerator write(final String name, final BigInteger value) { + delegate.write(name, value); + return this; + } + + @Override + public JsonGenerator write(final String name, final BigDecimal value) { + delegate.write(name, value); + return this; + } + + @Override + public JsonGenerator write(final String name, final int value) { + delegate.write(name, value); + return this; + } + + @Override + public JsonGenerator write(final String name, final long value) { + delegate.write(name, value); + return this; + } + + @Override + public JsonGenerator write(final String name, final double value) { + delegate.write(name, value); + return this; + } + + @Override + public JsonGenerator write(final String name, final boolean value) { + delegate.write(name, value); + return this; + } + + @Override + public JsonGenerator writeNull(final String name) { + delegate.writeNull(name); + return this; + } + + @Override + public JsonGenerator write(final JsonValue value) { + delegate.write(value); + return this; + } + + @Override + public JsonGenerator write(final String value) { + delegate.write(value); + return this; + } + + @Override + public JsonGenerator write(final BigDecimal value) { + delegate.write(value); + return this; + } + + @Override + public JsonGenerator write(final BigInteger value) { + delegate.write(value); + return this; + } + + @Override + public JsonGenerator write(final int value) { + delegate.write(value); + return this; + } + + @Override + public JsonGenerator write(final long value) { + delegate.write(value); + return this; + } + + @Override + public JsonGenerator write(final double value) { + delegate.write(value); + return this; + } + + @Override + public JsonGenerator write(boolean value) { + delegate.write(value); + return this; + } + + @Override + public JsonGenerator writeNull() { + delegate.writeNull(); + return this; + } + + @Override + public void close() { + delegate.close(); + } + + @Override + public void flush() { + delegate.flush(); + } + } + + private static class SkipLastWriteEndGenerator extends DelegatingGenerator { + private int level = -1; + + private SkipLastWriteEndGenerator(final JsonGenerator generator) { + super(generator); + } + + @Override + public JsonGenerator writeStartObject() { + level++; + if (level > 0) { + delegate.writeStartObject(); + } + return this; + } + + @Override + public JsonGenerator writeStartObject(final String name) { + level++; + if (level == 0) { + level++; // force a writeEnd since it will be a nested object and not the object we are writing + } + delegate.writeStartObject(name); + return this; + } + + @Override + public JsonGenerator writeStartArray() { + level++; + delegate.writeStartArray(); + return this; + } + + @Override + public JsonGenerator writeStartArray(final String name) { + delegate.writeStartArray(name); + level++; + return this; + } + + @Override + public JsonGenerator writeEnd() { + if (level > 0) { + delegate.writeEnd(); + } + level--; + return this; + } + } + + public static class SkipEnclosingWriteEnd extends DynamicMappingGenerator { + private static final Runnable NOOP = () -> { + }; + private final JsonGenerator rawGenerator; + + private SkipLastWriteEndGenerator skippingGenerator; + + public SkipEnclosingWriteEnd(final MappingGenerator delegate, final String keyName, final JsonGenerator generator) { + 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/MappingGeneratorImpl.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGeneratorImpl.java index 0a4ec42..b038f9d 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 @@ -45,7 +45,7 @@ public class MappingGeneratorImpl implements MappingGenerator { private final MapperConfig config; private final JsonGenerator generator; private final Mappings mappings; - + private final Boolean isDeduplicateObjects; private Map<Object, String> jsonPointers; @@ -348,7 +348,9 @@ public class MappingGeneratorImpl implements MappingGenerator { } if (classMapping.writer != null) { - classMapping.writer.writeJson(object, this); + final DynamicMappingGenerator gen = new DynamicMappingGenerator.SkipEnclosingWriteEnd(this, null, generator); + classMapping.writer.writeJson(object, gen); + gen.flushIfNeeded(); return; } if (classMapping.adapter != null) {