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 2017/04/18 19:18:04 UTC

johnzon git commit: JOHNZON-114 @JohnzonIgnoreNested support to cut common cycles (JPA for instance)

Repository: johnzon
Updated Branches:
  refs/heads/master dd294bd13 -> dc7306d0e


JOHNZON-114 @JohnzonIgnoreNested support to cut common cycles (JPA for instance)


Project: http://git-wip-us.apache.org/repos/asf/johnzon/repo
Commit: http://git-wip-us.apache.org/repos/asf/johnzon/commit/dc7306d0
Tree: http://git-wip-us.apache.org/repos/asf/johnzon/tree/dc7306d0
Diff: http://git-wip-us.apache.org/repos/asf/johnzon/diff/dc7306d0

Branch: refs/heads/master
Commit: dc7306d0e22647e7d48ff1e0958f984bbc369554
Parents: dd294bd
Author: rmannibucau <rm...@apache.org>
Authored: Tue Apr 18 21:17:52 2017 +0200
Committer: rmannibucau <rm...@apache.org>
Committed: Tue Apr 18 21:17:52 2017 +0200

----------------------------------------------------------------------
 .../johnzon/jsonb/JohnzonIgnoreNestedTest.java  | 69 ++++++++++++++++++++
 .../johnzon/mapper/JohnzonIgnoreNested.java     | 39 +++++++++++
 .../java/org/apache/johnzon/mapper/Mapper.java  | 21 +++---
 .../johnzon/mapper/MappingGeneratorImpl.java    | 43 ++++++------
 .../org/apache/johnzon/mapper/Mappings.java     | 13 ++--
 .../johnzon/mapper/JohnzonIgnoreNestedTest.java | 67 +++++++++++++++++++
 6 files changed, 220 insertions(+), 32 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/johnzon/blob/dc7306d0/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JohnzonIgnoreNestedTest.java
----------------------------------------------------------------------
diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JohnzonIgnoreNestedTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JohnzonIgnoreNestedTest.java
new file mode 100644
index 0000000..fb36804
--- /dev/null
+++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JohnzonIgnoreNestedTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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;
+
+import org.apache.johnzon.mapper.JohnzonIgnoreNested;
+import org.junit.Test;
+
+import javax.json.bind.Jsonb;
+import javax.json.bind.spi.JsonbProvider;
+import java.util.Collection;
+
+import static java.util.Collections.singletonList;
+import static org.junit.Assert.assertEquals;
+
+public class JohnzonIgnoreNestedTest {
+    @Test
+    public void ignoreNested() {
+        final To to = new To();
+        to.name = "to";
+
+        final From from = new From();
+        from.name = "from";
+
+        to.from = from;
+        to.froms = singletonList(from);
+        from.to = to;
+        from.tos = singletonList(to);
+
+        final Jsonb jsonb =  JsonbProvider.provider().create().build();
+        assertEquals("{\"from\":{\"name\":\"from\"},\"froms\":[{\"name\":\"from\"}],\"name\":\"to\"}", jsonb.toJson(to));
+        assertEquals("{\"name\":\"from\",\"to\":{\"name\":\"to\"},\"tos\":[{\"name\":\"to\"}]}", jsonb.toJson(from));
+    }
+
+    public static class To {
+        public String name;
+
+        @JohnzonIgnoreNested(properties = {"to", "tos"})
+        public From from;
+
+        @JohnzonIgnoreNested(properties = {"to", "tos"})
+        public Collection<From> froms;
+    }
+
+    public static class From {
+        public String name;
+
+        @JohnzonIgnoreNested(properties = {"from", "froms"})
+        public To to;
+
+        @JohnzonIgnoreNested(properties = {"from", "froms"})
+        public Collection<To> tos;
+    }
+}

http://git-wip-us.apache.org/repos/asf/johnzon/blob/dc7306d0/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JohnzonIgnoreNested.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JohnzonIgnoreNested.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JohnzonIgnoreNested.java
new file mode 100644
index 0000000..f1d16a7
--- /dev/null
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/JohnzonIgnoreNested.java
@@ -0,0 +1,39 @@
+/*
+ * 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.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Only used during serialization for now.
+ */
+@Target({ METHOD, FIELD, ANNOTATION_TYPE })
+@Retention(RUNTIME)
+public @interface JohnzonIgnoreNested {
+    /**
+     * @return the array of properties to avoid in the nested type.
+     */
+    String[] properties() default {};
+}

http://git-wip-us.apache.org/repos/asf/johnzon/blob/dc7306d0/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mapper.java
----------------------------------------------------------------------
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 9fe2bc8..b245e96 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
@@ -85,7 +85,7 @@ public class Mapper implements Closeable {
 
     public <T> void writeArray(final Collection<T> object, final Writer stream) {
         JsonGenerator generator = generatorFactory.createGenerator(stream(stream));
-        writeObject(object, generator);
+        writeObject(object, generator, null);
     }
 
     public <T> void writeIterable(final Iterable<T> object, final OutputStream stream) {
@@ -94,7 +94,7 @@ public class Mapper implements Closeable {
 
     public <T> void writeIterable(final Iterable<T> object, final Writer stream) {
         JsonGenerator generator = generatorFactory.createGenerator(stream(stream));
-        writeObject(object, generator);
+        writeObject(object, generator, null);
     }
 
     public void writeObject(final Object object, final Writer stream) {
@@ -125,21 +125,21 @@ public class Mapper implements Closeable {
         }
 
         final JsonGenerator generator = generatorFactory.createGenerator(stream(stream));
-        writeObject(object, generator);
+        writeObject(object, generator, null);
     }
 
     public void writeObject(final Object object, final OutputStream stream) {
         final JsonGenerator generator = generatorFactory.createGenerator(stream(stream), config.getEncoding());
-        writeObject(object, generator);
+        writeObject(object, generator, null);
     }
 
-    private void writeObject(final Object object, final JsonGenerator generator) {
+    private void writeObject(final Object object, final JsonGenerator generator, final Collection<String> ignored) {
         MappingGeneratorImpl mappingGenerator = new MappingGeneratorImpl(config, generator, mappings);
 
-        RuntimeException originalException = null;
+        Throwable originalException = null;
         try {
-            mappingGenerator.doWriteObject(object, generator, true);
-        } catch (RuntimeException e) {
+            mappingGenerator.doWriteObject(object, generator, true, ignored);
+        } catch (final Error | RuntimeException e) {
             originalException = e;
         } finally {
 
@@ -148,7 +148,10 @@ public class Mapper implements Closeable {
             } catch (JsonException e) {
 
                 if (originalException != null) {
-                    throw originalException;
+                    if (RuntimeException.class.isInstance(originalException)) {
+                        throw RuntimeException.class.cast(originalException);
+                    }
+                    throw Error.class.cast(originalException); // stackoverflow falls here
                 } else {
                     throw e;
                 }

http://git-wip-us.apache.org/repos/asf/johnzon/blob/dc7306d0/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingGeneratorImpl.java
----------------------------------------------------------------------
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 38b5315..107307a 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
@@ -54,12 +54,12 @@ public class MappingGeneratorImpl implements MappingGenerator {
         } else if (object instanceof JsonValue) {
             generator.write((JsonValue) object);
         } else {
-            doWriteObject(object, generator, false);
+            doWriteObject(object, generator, false, null);
         }
         return this;
     }
 
-    public void doWriteObject(Object object, JsonGenerator generator, boolean writeBody) {
+    public void doWriteObject(Object object, JsonGenerator generator, boolean writeBody, final Collection<String> ignoredProperties) {
         try {
             if (object instanceof Map) {
                 if (writeBody) {
@@ -85,7 +85,7 @@ public class MappingGeneratorImpl implements MappingGenerator {
             }
 
             if (object instanceof Iterable) {
-                doWriteIterable((Iterable) object);
+                doWriteIterable((Iterable) object, ignoredProperties);
                 return;
             }
 
@@ -97,7 +97,7 @@ public class MappingGeneratorImpl implements MappingGenerator {
             if (writeBody && objectConverter != null) {
                 objectConverter.writeJson(object, this);
             } else {
-                doWriteObjectBody(object);
+                doWriteObjectBody(object, ignoredProperties);
             }
 
             if (writeBody) {
@@ -132,7 +132,7 @@ public class MappingGeneratorImpl implements MappingGenerator {
             final boolean map = clazz || primitive || array || collection ? false : Map.class.isAssignableFrom(valueClass);
             writeValue(valueClass,
                     primitive, array, collection, map, itemConverter,
-                    key == null ? "null" : key.toString(), value, null);
+                    key == null ? "null" : key.toString(), value, null, null);
         }
         return generator;
     }
@@ -224,7 +224,7 @@ public class MappingGeneratorImpl implements MappingGenerator {
     }
 
 
-    private void doWriteObjectBody(final Object object) throws IllegalAccessException, InvocationTargetException {
+    private void doWriteObjectBody(final Object object, final Collection<String> ignored) throws IllegalAccessException, InvocationTargetException {
         final Class<?> objectClass = object.getClass();
         final Mappings.ClassMapping classMapping = mappings.findOrCreateClassMapping(objectClass);
         if (classMapping == null) {
@@ -236,12 +236,15 @@ public class MappingGeneratorImpl implements MappingGenerator {
             return;
         }
         if (classMapping.adapter != null) {
-            doWriteObjectBody(classMapping.adapter.to(object));
+            doWriteObjectBody(classMapping.adapter.to(object), ignored);
             return;
         }
 
         for (final Map.Entry<String, Mappings.Getter> getterEntry : classMapping.getters.entrySet()) {
             final Mappings.Getter getter = getterEntry.getValue();
+            if (ignored != null && ignored.contains(getterEntry.getKey())) {
+                continue;
+            }
             if (getter.version >= 0 && config.getVersion() >= getter.version) {
                 continue;
             }
@@ -268,7 +271,8 @@ public class MappingGeneratorImpl implements MappingGenerator {
                     getter.collection, getter.map,
                     getter.itemConverter,
                     getterEntry.getKey(),
-                    val, getter.objectConverter);
+                    val, getter.objectConverter,
+                    getter.ignoreNested);
         }
 
         // @JohnzonAny doesn't respect comparator since it is a map and not purely in the model we append it after and
@@ -286,7 +290,8 @@ public class MappingGeneratorImpl implements MappingGenerator {
                             final boolean collection, final boolean map,
                             final Adapter itemConverter,
                             final String key, final Object value,
-                            final ObjectConverter.Writer objectConverter) throws InvocationTargetException, IllegalAccessException {
+                            final ObjectConverter.Writer objectConverter,
+                            final Collection<String> ignoredProperties) throws InvocationTargetException, IllegalAccessException {
         if (array) {
             final int length = Array.getLength(value);
             if (length == 0 && config.isSkipEmptyArray()) {
@@ -306,14 +311,14 @@ public class MappingGeneratorImpl implements MappingGenerator {
             generator.writeStartArray(key);
             for (int i = 0; i < length; i++) {
                 final Object o = Array.get(value, i);
-                writeItem(itemConverter != null ? itemConverter.from(o) : o);
+                writeItem(itemConverter != null ? itemConverter.from(o) : o, ignoredProperties);
             }
             generator.writeEnd();
             return;
         } else if (collection) {
             generator.writeStartArray(key);
             for (final Object o : Collection.class.cast(value)) {
-                writeItem(itemConverter != null ? itemConverter.from(o) : o);
+                writeItem(itemConverter != null ? itemConverter.from(o) : o, ignoredProperties);
             }
             generator.writeEnd();
             return;
@@ -332,7 +337,7 @@ public class MappingGeneratorImpl implements MappingGenerator {
                 if (writePrimitives(key, adapted.getClass(), adapted)) {
                     return;
                 }
-                writeValue(String.class, true, false, false, false, null, key, adapted, null);
+                writeValue(String.class, true, false, false, false, null, key, adapted, null, ignoredProperties);
                 return;
             } else {
 
@@ -352,15 +357,15 @@ public class MappingGeneratorImpl implements MappingGenerator {
                 return;
             }
             generator.writeStartObject(key);
-            doWriteObjectBody(value);
+            doWriteObjectBody(value, ignoredProperties);
             generator.writeEnd();
         }
     }
 
-    private void writeItem(final Object o) {
+    private void writeItem(final Object o, final Collection<String> ignoredProperties) {
         if (!writePrimitives(o)) {
             if (Collection.class.isInstance(o)) {
-                doWriteIterable(Collection.class.cast(o));
+                doWriteIterable(Collection.class.cast(o), ignoredProperties);
             } else if (o != null && o.getClass().isArray()) {
                 final int length = Array.getLength(o);
                 if (length > 0 || !config.isSkipEmptyArray()) {
@@ -370,7 +375,7 @@ public class MappingGeneratorImpl implements MappingGenerator {
                         if (t == null) {
                             generator.writeNull();
                         } else {
-                            writeItem(t);
+                            writeItem(t, ignoredProperties);
                         }
                     }
                     generator.writeEnd();
@@ -378,12 +383,12 @@ public class MappingGeneratorImpl implements MappingGenerator {
             } else if (o == null) {
                 generator.writeNull();
             } else {
-                doWriteObject(o, generator, true);
+                doWriteObject(o, generator, true, ignoredProperties);
             }
         }
     }
 
-    private <T> void doWriteIterable(final Iterable<T> object) {
+    private <T> void doWriteIterable(final Iterable<T> object, final Collection<String> ignoredProperties) {
         if (object == null) {
             generator.writeStartArray().writeEnd();
         } else {
@@ -395,7 +400,7 @@ public class MappingGeneratorImpl implements MappingGenerator {
                     if (t == null) {
                         generator.writeNull();
                     } else {
-                        writeItem(t);
+                        writeItem(t, ignoredProperties);
                     }
                 }
             }

http://git-wip-us.apache.org/repos/asf/johnzon/blob/dc7306d0/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
index 35dea61..51ae62c 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/Mappings.java
@@ -104,18 +104,20 @@ public class Mappings {
         public final boolean array;
         public final boolean map;
         public final boolean collection;
+        public final Collection<String> ignoreNested;
 
         public Getter(final AccessMode.Reader reader,
                       final boolean primitive, final boolean array,
                       final boolean collection, final boolean map,
                       final MapperConverter converter,
                       final ObjectConverter.Writer providedObjectConverter,
-                      final int version) {
+                      final int version, final String[] ignoreNested) {
             this.reader = reader;
             this.version = version;
             this.array = array;
             this.collection = collection;
             this.primitive = primitive;
+            this.ignoreNested = ignoreNested == null || ignoreNested.length == 0 ? null : new HashSet<>(asList(ignoreNested));
 
             Adapter theConverter = null;
             Adapter theItemConverter = null;
@@ -387,7 +389,7 @@ public class Mappings {
                 accessMode.findWriter(clazz),
                 anyGetter != null ? new Getter(
                         new MethodAccessMode.MethodReader(anyGetter, anyGetter.getReturnType()),
-                        false, false, false, true, null, null, -1) : null,
+                        false, false, false, true, null, null, -1, null) : null,
                 accessMode.findAnySetter(clazz));
 
         accessMode.afterParsed(clazz);
@@ -437,6 +439,7 @@ public class Mappings {
                                    final AccessMode.Reader value,
                                    final boolean copyDate) {
         final JohnzonIgnore readIgnore = value.getAnnotation(JohnzonIgnore.class);
+        final JohnzonIgnoreNested ignoreNested = value.getAnnotation(JohnzonIgnoreNested.class);
         if (readIgnore == null || readIgnore.minVersion() >= 0) {
             final Class<?> returnType = Class.class.isInstance(value.getType()) ? Class.class.cast(value.getType()) : null;
             final ParameterizedType pt = ParameterizedType.class.isInstance(value.getType()) ? ParameterizedType.class.cast(value.getType()) : null;
@@ -447,7 +450,8 @@ public class Mappings {
                     (pt != null && Map.class.isAssignableFrom(Class.class.cast(pt.getRawType())))
                             || (returnType != null && Map.class.isAssignableFrom(returnType)),
                     findConverter(copyDate, value), value.findObjectConverterWriter(),
-                    readIgnore != null ? readIgnore.minVersion() : -1);
+                    readIgnore != null ? readIgnore.minVersion() : -1,
+                    ignoreNested != null ? ignoreNested.properties() : null);
             getters.put(key, getter);
         }
     }
@@ -494,7 +498,8 @@ public class Mappings {
 
         final Getter getter = getters.get(key);
         final MapBuilderReader newReader = new MapBuilderReader(objectGetters, path, config.getVersion());
-        getters.put(key, new Getter(getter == null ? newReader : new CompositeReader(getter.reader, newReader), false, false, false, true, null, null, -1));
+        getters.put(key, new Getter(getter == null ? newReader :
+                new CompositeReader(getter.reader, newReader), false, false, false, true, null, null, -1, null));
 
         final Setter newSetter = setters.get(key);
         final MapUnwrapperWriter newWriter = new MapUnwrapperWriter(objectSetters, path);

http://git-wip-us.apache.org/repos/asf/johnzon/blob/dc7306d0/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/JohnzonIgnoreNestedTest.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/JohnzonIgnoreNestedTest.java b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/JohnzonIgnoreNestedTest.java
new file mode 100644
index 0000000..986c744
--- /dev/null
+++ b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/JohnzonIgnoreNestedTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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 org.junit.Test;
+
+import java.util.Collection;
+import java.util.Comparator;
+
+import static java.util.Collections.singletonList;
+import static org.junit.Assert.assertEquals;
+
+public class JohnzonIgnoreNestedTest {
+    @Test
+    public void ignoreNested() {
+        final To to = new To();
+        to.name = "to";
+
+        final From from = new From();
+        from.name = "from";
+
+        to.from = from;
+        to.froms = singletonList(from);
+        from.to = to;
+        from.tos = singletonList(to);
+
+        final Mapper mapper = new MapperBuilder().setAttributeOrder(Comparator.naturalOrder()).build();
+        assertEquals("{\"from\":{\"name\":\"from\"},\"froms\":[{\"name\":\"from\"}],\"name\":\"to\"}", mapper.writeObjectAsString(to));
+        assertEquals("{\"name\":\"from\",\"to\":{\"name\":\"to\"},\"tos\":[{\"name\":\"to\"}]}", mapper.writeObjectAsString(from));
+    }
+
+    public static class To {
+        public String name;
+
+        @JohnzonIgnoreNested(properties = {"to", "tos"})
+        public From from;
+
+        @JohnzonIgnoreNested(properties = {"to", "tos"})
+        public Collection<From> froms;
+    }
+
+    public static class From {
+        public String name;
+
+        @JohnzonIgnoreNested(properties = {"from", "froms"})
+        public To to;
+
+        @JohnzonIgnoreNested(properties = {"from", "froms"})
+        public Collection<To> tos;
+    }
+}