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 389b696 [JOHNZON-344] implicit object opening for serializer starting with writeStartArray(key) 389b696 is described below commit 389b6964b2ac2e5f4d12007ae84296d0b911449d Author: Romain Manni-Bucau <rmannibu...@gmail.com> AuthorDate: Wed May 12 13:38:53 2021 +0200 [JOHNZON-344] implicit object opening for serializer starting with writeStartArray(key) --- .../jsonb/api/experimental/JsonbExtensionTest.java | 90 +++++++++++++++++++++- .../johnzon/mapper/DynamicMappingGenerator.java | 15 +++- 2 files changed, 102 insertions(+), 3 deletions(-) diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/api/experimental/JsonbExtensionTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/api/experimental/JsonbExtensionTest.java index a29b4ba..a2d612c 100644 --- a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/api/experimental/JsonbExtensionTest.java +++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/api/experimental/JsonbExtensionTest.java @@ -161,6 +161,33 @@ public class JsonbExtensionTest { assertEquals(attribs, deserialized); } + @Test + public void complexNoOpenCloseDeserializer() { + final LocalDate date = LocalDate.of(2021, Month.valueOf("MAY"), 12); + final LocalTime time = LocalTime.of(1, 2, 0, 0); + final OffsetDateTime offsetDateTime = OffsetDateTime.of(date, time, ZoneOffset.UTC); + final LocalDateTime localDateTime = LocalDateTime.of(date, time); + + final Sink3 attribs = new Sink3(); + attribs.attributes.put("ldateTime", new Wrapper3(singletonList(offsetDateTime))); + attribs.attributes.put("llocalDate", new Wrapper3(singletonList(date))); + attribs.attributes.put("llocalTime", new Wrapper3(singletonList(time))); + attribs.attributes.put("llocalDateTime", new Wrapper3(singletonList(localDateTime))); + + final String json = jsonb.toJson(attribs); + assertJsonEquals("" + + "{\"attributes\":{" + + "\"ldateTime\":{\"value\":[\"2021-05-12T01:02Z\"]}" + + ",\"llocalDate\":{\"value\":[\"2021-05-12\"]}" + + ",\"llocalDateTime\":{\"value\":[\"2021-05-12T01:02\"]}" + + ",\"llocalTime\":{\"value\":[\"01:02\"]}" + + "}}", + json); + + final Sink3 deserialized = jsonb.fromJson(json, Sink3.class); + assertEquals(attribs, deserialized); + } + // assumes json are valid but enables nicer diff private void assertJsonEquals(final String expected, final String actual) { final JsonWriterFactory writerFactory = Json.createWriterFactory(singletonMap(JsonGenerator.PRETTY_PRINTING, true)); @@ -261,6 +288,57 @@ public class JsonbExtensionTest { } } + @JsonbTypeSerializer(OpenWrapperCodec.class) + @JsonbTypeDeserializer(OpenWrapperCodec.class) + public static class Wrapper3 extends Wrapper2 { + public Wrapper3() { + super(); + } + + public Wrapper3(final Object value) { + super(value); + } + } + + public static class Sink3 implements Serializable { + private Map<String, Wrapper3> attributes = new TreeMap<>(); + + public Object get(final String name) { + final Wrapper3 att = attributes.get(name); + return att != null ? att.getValue() : null; + } + + public void setAttributes(final Map<String, Wrapper3> attributes) { + this.attributes = attributes; + } + + public Map<String, Wrapper3> getAttributes() { + return attributes; + } + + @Override + public boolean equals(final Object obj) { // for test + return Sink3.class.isInstance(obj) && Objects.equals(Sink3.class.cast(obj).attributes, attributes); + } + + @Override + public int hashCode() { // for test + return super.hashCode(); + } + } + + public static class OpenWrapperCodec extends WrapperCodec { + @Override + protected void afterList(final JsonGenerator generator) { + // no-op + } + + @Override + protected void beforeList(final JsonGenerator generator) { + // no-op + } + } + public static class WrapperCodec implements JsonbDeserializer<Wrapper2>, JsonbSerializer<Wrapper2> { @Override public void serialize(final Wrapper2 wrapper, final JsonGenerator generator, final SerializationContext ctx) { @@ -271,9 +349,9 @@ public class JsonbExtensionTest { if (value instanceof List) { final List<Object> list = (List<Object>) value; if (!list.isEmpty()) { - generator.writeStartObject(); + beforeList(generator); writeArray(generator, list); - generator.writeEnd(); + afterList(generator); } } else if (value instanceof String) { generator.write(markerFor(value), (String) value); @@ -282,6 +360,14 @@ public class JsonbExtensionTest { } } + protected void afterList(final JsonGenerator generator) { + generator.writeEnd(); + } + + protected void beforeList(final JsonGenerator generator) { + generator.writeStartObject(); + } + private void writeArray(final JsonGenerator generator, final List<Object> list) { generator.writeStartArray("a" + markerFor(list.get(0))); for (final Object o : list) { 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 ccf16c3..f5cb2ea 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 @@ -92,6 +92,7 @@ public class DynamicMappingGenerator implements MappingGenerator { private final String keyIfNoObject; private WritingState state = WritingState.NONE; // todo: we need a stack (linkedlist) here to be accurate private int nested = 0; + private boolean implicitStart; private InObjectOrPrimitiveJsonGenerator(final JsonGenerator generator, final Runnable writeStart, final String keyName, final Runnable writeEnd) { @@ -148,6 +149,10 @@ public class DynamicMappingGenerator implements MappingGenerator { @Override public JsonGenerator writeStartArray(final String name) { + if (state == WritingState.NONE && !implicitStart) { // force an enclosing object since we write in an object (we have a key) + writeStartObject(); + implicitStart = true; + } if (state != WritingState.NONE) { nested++; } @@ -337,7 +342,11 @@ public class DynamicMappingGenerator implements MappingGenerator { @Override public JsonGenerator writeEnd() { - return doWriteEnd(false); + final JsonGenerator generator = doWriteEnd(false); + if (nested == 0 && implicitStart) { + doWriteEnd(false); + } + return generator; } private JsonGenerator doWriteEnd(final boolean useDelegate) { @@ -562,6 +571,10 @@ public class DynamicMappingGenerator implements MappingGenerator { public JsonGenerator writeEnd() { if (level > 0) { delegate.writeEnd(); + } else if (level == 0 && + InObjectOrPrimitiveJsonGenerator.class.isInstance(delegate) && // normally always true + InObjectOrPrimitiveJsonGenerator.class.cast(delegate).implicitStart) { + delegate.writeEnd(); } level--; return this;