You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@johnzon.apache.org by rm...@apache.org on 2019/04/02 13:17:24 UTC

[johnzon] branch master updated: JOHNZON-207 tolerate primitive in objects in serializers

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 4f73100  JOHNZON-207 tolerate primitive in objects in serializers
4f73100 is described below

commit 4f73100bdb5a094c7f05d361b0753e5bfe031566
Author: Romain Manni-Bucau <rm...@gmail.com>
AuthorDate: Tue Apr 2 15:17:14 2019 +0200

    JOHNZON-207 tolerate primitive in objects in serializers
---
 .../org/apache/johnzon/jsonb/JohnzonBuilder.java   |   6 +-
 .../org/apache/johnzon/jsonb/SerializerTest.java   |  38 ++-
 .../johnzon/mapper/DynamicMappingGenerator.java    | 277 +++++++++++++++++++++
 .../johnzon/mapper/MappingGeneratorImpl.java       |  37 +--
 4 files changed, 334 insertions(+), 24 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 a12608d..d146d79 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
@@ -284,8 +284,10 @@ 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) -> s.serialize(instance, jsonbGenerator.getJsonGenerator(), new JohnzonSerializationContext(jsonbGenerator)));
+                    Class.class.cast(args[0]), (ObjectConverter.Writer) (instance, jsonbGenerator) ->
+                        s.serialize(
+                                instance, jsonbGenerator.getJsonGenerator(),
+                                new JohnzonSerializationContext(jsonbGenerator)));
             });
         });
         config.getProperty(JsonbConfig.DESERIALIZERS).map(JsonbDeserializer[].class::cast).ifPresent(deserializers -> {
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 963a41e..030f8fc 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
@@ -18,7 +18,14 @@
  */
 package org.apache.johnzon.jsonb;
 
-import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
 
 import javax.json.bind.Jsonb;
 import javax.json.bind.JsonbBuilder;
@@ -30,13 +37,8 @@ import javax.json.bind.serializer.JsonbSerializer;
 import javax.json.bind.serializer.SerializationContext;
 import javax.json.stream.JsonGenerator;
 import javax.json.stream.JsonParser;
-import java.lang.reflect.Type;
-import java.util.ArrayList;
-import java.util.List;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import org.junit.Test;
 
 public class SerializerTest {
 
@@ -107,6 +109,15 @@ public class SerializerTest {
         assertEquals("{\"elems\":[\"WRONG\",null]}", jsonb.toJson(wrapper));
     }
 
+    @Test
+    public void uuid() throws Exception {
+        final Jsonb jsonb = JsonbBuilder.create();
+        final UUIDWrapper wrapper = new UUIDWrapper();
+        wrapper.uuid = UUID.randomUUID();
+        assertEquals("{\"uuid\":\"4a34a0e8-c0c1-45f7-9fa4-2e28f15fd9be\"}", jsonb.toJson(wrapper));
+        jsonb.close();
+    }
+
 
     public static class Foo {
         public String name;
@@ -114,7 +125,20 @@ public class SerializerTest {
         public boolean flag;
     }
 
+    public static class UUIDSerializer implements JsonbSerializer<UUID> {
+        @Override
+        public void serialize(final UUID obj, final JsonGenerator generator, final SerializationContext ctx) {
+            generator.write(obj.toString());
+        }
+    }
+
+    public static class UUIDWrapper {
+        @JsonbTypeSerializer(UUIDSerializer.class)
+        public UUID uuid;
+    }
+
     public static class Wrapper {
+
         @JsonbTypeSerializer(FooSer.class)
         @JsonbTypeDeserializer(FooDeser.class)
         public Foo foo;
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
new file mode 100644
index 0000000..fff01e6
--- /dev/null
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/DynamicMappingGenerator.java
@@ -0,0 +1,277 @@
+/*
+ * 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 java.math.BigDecimal;
+import java.math.BigInteger;
+
+import javax.json.JsonValue;
+import javax.json.stream.JsonGenerator;
+
+public class DynamicMappingGenerator implements MappingGenerator {
+    private final MappingGenerator delegate;
+    private final Runnable writeStart;
+    private final Runnable writeEnd;
+    private final String keyName;
+
+    private InObjectOrPrimitiveJsonGenerator generator;
+
+    public DynamicMappingGenerator(final MappingGenerator delegate,
+                                   final Runnable writeStart,
+                                   final Runnable writeEnd,
+                                   final String keyName) {
+        this.delegate = delegate;
+        this.writeStart = writeStart;
+        this.writeEnd = writeEnd;
+        this.keyName = keyName;
+    }
+
+    @Override
+    public JsonGenerator getJsonGenerator() {
+        return generator == null ? generator = new InObjectOrPrimitiveJsonGenerator(
+                delegate.getJsonGenerator(), writeStart, keyName) : generator;
+    }
+
+    @Override
+    public MappingGenerator writeObject(final Object o, final JsonGenerator generator) {
+        if (this.generator != null && this.generator != generator && this.generator.delegate != generator) {
+            this.generator = null;
+        }
+        getJsonGenerator(); // ensure we wrap it
+
+        final MappingGenerator mappingGenerator = delegate.writeObject(o, this.generator);
+        flushIfNeeded();
+        return mappingGenerator;
+    }
+
+    public void flushIfNeeded() {
+        if (this.generator.state == WritingState.WROTE_START_OBJECT) {
+            writeEnd.run();
+            this.generator.state = WritingState.NONE;
+        }
+    }
+
+    private enum WritingState {
+        NONE, WROTE_START_OBJECT, PRIMITIVE
+    }
+
+    private static class InObjectOrPrimitiveJsonGenerator implements JsonGenerator {
+        private final JsonGenerator delegate;
+        private final Runnable writeStart;
+        private final String keyIfNoObject;
+        private WritingState state = WritingState.NONE;
+
+        private InObjectOrPrimitiveJsonGenerator(final JsonGenerator generator, final Runnable writeStart,
+                                                 final String keyName) {
+            this.delegate = generator;
+            this.writeStart = writeStart;
+            this.keyIfNoObject = keyName;
+        }
+
+        private void ensureStart() {
+            if (state == WritingState.WROTE_START_OBJECT) {
+                return;
+            }
+            writeStart.run();
+            state = WritingState.WROTE_START_OBJECT;
+        }
+
+        @Override
+        public JsonGenerator writeStartObject() {
+            return delegate.writeStartObject();
+        }
+
+        @Override
+        public JsonGenerator writeStartObject(final String name) {
+            ensureStart();
+            return delegate.writeStartObject(name);
+        }
+
+        @Override
+        public JsonGenerator writeStartArray() {
+            return delegate.writeStartArray();
+        }
+
+        @Override
+        public JsonGenerator writeStartArray(final String name) {
+            ensureStart();
+            return delegate.writeStartArray(name);
+        }
+
+        @Override
+        public JsonGenerator writeKey(final String name) {
+            ensureStart();
+            return delegate.writeKey(name);
+        }
+
+        @Override
+        public JsonGenerator write(final String name, final JsonValue value) {
+            ensureStart();
+            return delegate.write(name, value);
+        }
+
+        @Override
+        public JsonGenerator write(final String name, final String value) {
+            ensureStart();
+            return delegate.write(name, value);
+        }
+
+        @Override
+        public JsonGenerator write(final String name, final BigInteger value) {
+            ensureStart();
+            return delegate.write(name, value);
+        }
+
+        @Override
+        public JsonGenerator write(final String name, final BigDecimal value) {
+            ensureStart();
+            return delegate.write(name, value);
+        }
+
+        @Override
+        public JsonGenerator write(final String name, final int value) {
+            ensureStart();
+            return delegate.write(name, value);
+        }
+
+        @Override
+        public JsonGenerator write(final String name, final long value) {
+            ensureStart();
+            return delegate.write(name, value);
+        }
+
+        @Override
+        public JsonGenerator write(final String name, final double value) {
+            ensureStart();
+            return delegate.write(name, value);
+        }
+
+        @Override
+        public JsonGenerator write(final String name, final boolean value) {
+            ensureStart();
+            return delegate.write(name, value);
+        }
+
+        @Override
+        public JsonGenerator writeNull(final String name) {
+            ensureStart();
+            return delegate.writeNull(name);
+        }
+
+        @Override
+        public JsonGenerator writeEnd() {
+            return delegate.writeEnd();
+        }
+
+        @Override
+        public JsonGenerator write(final JsonValue value) {
+            if (isWritingPrimitive()) {
+                state = WritingState.PRIMITIVE;
+                return delegate.write(keyIfNoObject, value);
+            }
+            return delegate.write(value);
+        }
+
+        @Override
+        public JsonGenerator write(final String value) {
+            if (isWritingPrimitive()) {
+                state = WritingState.PRIMITIVE;
+                return delegate.write(keyIfNoObject, value);
+            }
+            return delegate.write(value);
+        }
+
+        @Override
+        public JsonGenerator write(final BigDecimal value) {
+            if (isWritingPrimitive()) {
+                state = WritingState.PRIMITIVE;
+                return delegate.write(keyIfNoObject, value);
+            }
+            return delegate.write(value);
+        }
+
+        @Override
+        public JsonGenerator write(final BigInteger value) {
+            if (isWritingPrimitive()) {
+                state = WritingState.PRIMITIVE;
+                return delegate.write(keyIfNoObject, value);
+            }
+            return delegate.write(value);
+        }
+
+        @Override
+        public JsonGenerator write(final int value) {
+            if (isWritingPrimitive()) {
+                state = WritingState.PRIMITIVE;
+                return delegate.write(keyIfNoObject, value);
+            }
+            return delegate.write(value);
+        }
+
+        @Override
+        public JsonGenerator write(final long value) {
+            if (isWritingPrimitive()) {
+                state = WritingState.PRIMITIVE;
+                return delegate.write(keyIfNoObject, value);
+            }
+            return delegate.write(value);
+        }
+
+        @Override
+        public JsonGenerator write(final double value) {
+            if (isWritingPrimitive()) {
+                state = WritingState.PRIMITIVE;
+                return delegate.write(keyIfNoObject, value);
+            }
+            return delegate.write(value);
+        }
+
+        @Override
+        public JsonGenerator write(boolean value) {
+            if (isWritingPrimitive()) {
+                state = WritingState.PRIMITIVE;
+                return delegate.write(keyIfNoObject, value);
+            }
+            return delegate.write(value);
+        }
+
+        @Override
+        public JsonGenerator writeNull() {
+            if (isWritingPrimitive()) {
+                state = WritingState.PRIMITIVE;
+                return delegate.writeNull(keyIfNoObject);
+            }
+            return delegate.writeNull();
+        }
+
+        private boolean isWritingPrimitive() {
+            return state == WritingState.NONE && keyIfNoObject != null;
+        }
+
+        @Override
+        public void close() {
+            delegate.close();
+        }
+
+        @Override
+        public void flush() {
+            delegate.flush();
+        }
+    }
+}
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 d35a580..5c17a4f 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
@@ -105,19 +105,24 @@ public class MappingGeneratorImpl implements MappingGenerator {
                 return;
             }
 
-            if (writeBody) {
-                generator.writeStartObject();
-            }
-
             ObjectConverter.Writer objectConverter = config.findObjectConverterWriter(objectClass);
             if (writeBody && objectConverter != null) {
-                objectConverter.writeJson(object, this);
+                if (!writeBody) {
+                    objectConverter.writeJson(object, this);
+                } else {
+                    final DynamicMappingGenerator dynamicMappingGenerator = new DynamicMappingGenerator(this,
+                            generator::writeStartObject, generator::writeEnd, null);
+                    objectConverter.writeJson(object, dynamicMappingGenerator);
+                    dynamicMappingGenerator.flushIfNeeded();
+                }
             } else {
+                if (writeBody) {
+                    generator.writeStartObject();
+                }
                 doWriteObjectBody(object, ignoredProperties, jsonPointer);
-            }
-
-            if (writeBody) {
-                generator.writeEnd();
+                if (writeBody) {
+                    generator.writeEnd();
+                }
             }
         } catch (final InvocationTargetException e) {
             throw new MapperException(e);
@@ -344,9 +349,10 @@ public class MappingGeneratorImpl implements MappingGenerator {
                     }
 
                     if (objectConverterToUse != null) {
-                        generator.writeStartObject();
-                        objectConverterToUse.writeJson(o, this);
-                        generator.writeEnd();
+                        final DynamicMappingGenerator dynamicMappingGenerator = new DynamicMappingGenerator(this,
+                                generator::writeStartObject, generator::writeEnd, null);
+                        objectConverterToUse.writeJson(o, dynamicMappingGenerator);
+                        dynamicMappingGenerator.flushIfNeeded();
                     } else {
                         writeItem(itemConverter != null ? itemConverter.from(o) : o, ignoredProperties,
                                 isDeduplicateObjects ? new JsonPointerTracker(jsonPointer, i) : null);
@@ -377,9 +383,10 @@ public class MappingGeneratorImpl implements MappingGenerator {
                 }
 
                 if (objectConverterToUse != null) {
-                    generator.writeStartObject(key);
-                    objectConverterToUse.writeJson(value, this);
-                    generator.writeEnd();
+                    final DynamicMappingGenerator generator = new DynamicMappingGenerator(this,
+                            () -> this.generator.writeStartObject(key), this.generator::writeEnd, key);
+                    objectConverterToUse.writeJson(value, generator);
+                    generator.flushIfNeeded();
                     return;
                 }
             }