You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@johnzon.apache.org by st...@apache.org on 2017/09/22 21:26:04 UTC

johnzon git commit: JOHNZON-135 support recursive structures

Repository: johnzon
Updated Branches:
  refs/heads/master d7a4a2f9d -> 0091f2019


JOHNZON-135 support recursive structures

this is the write part for now.
We still need to support de-serialisation


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

Branch: refs/heads/master
Commit: 0091f20199803b9e32bdfe3d696e6109de0ad8b8
Parents: d7a4a2f
Author: Mark Struberg <st...@apache.org>
Authored: Fri Sep 22 23:23:41 2017 +0200
Committer: Mark Struberg <st...@apache.org>
Committed: Fri Sep 22 23:23:41 2017 +0200

----------------------------------------------------------------------
 .../johnzon/mapper/MappingGeneratorImpl.java    | 62 +++++++++++++++-----
 .../johnzon/mapper/CircularExceptionTest.java   | 37 ++++++++++++
 2 files changed, 85 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/johnzon/blob/0091f201/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 92609c9..f23344c 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
@@ -18,6 +18,7 @@
  */
 package org.apache.johnzon.mapper;
 
+import org.apache.johnzon.core.JsonPointerUtil;
 import org.apache.johnzon.mapper.internal.AdapterKey;
 
 import javax.json.JsonValue;
@@ -28,12 +29,14 @@ import java.lang.reflect.InvocationTargetException;
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.Map;
 
 public class MappingGeneratorImpl implements MappingGenerator {
     private final MapperConfig config;
     private final JsonGenerator generator;
     private final Mappings mappings;
+    private Map<Object, String> jsonPointers = new HashMap<>();
 
 
     MappingGeneratorImpl(MapperConfig config, JsonGenerator jsonGenerator, final Mappings mappings) {
@@ -60,6 +63,7 @@ public class MappingGeneratorImpl implements MappingGenerator {
     }
 
     public void doWriteObject(Object object, JsonGenerator generator, boolean writeBody, final Collection<String> ignoredProperties) {
+
         try {
             if (object instanceof Map) {
                 if (writeBody) {
@@ -97,7 +101,7 @@ public class MappingGeneratorImpl implements MappingGenerator {
             if (writeBody && objectConverter != null) {
                 objectConverter.writeJson(object, this);
             } else {
-                doWriteObjectBody(object, ignoredProperties);
+                doWriteObjectBody(object, ignoredProperties, "/");
             }
 
             if (writeBody) {
@@ -132,7 +136,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, null);
+                    key == null ? "null" : key.toString(), value, null, null, null);
         }
         return generator;
     }
@@ -224,7 +228,13 @@ public class MappingGeneratorImpl implements MappingGenerator {
     }
 
 
-    private void doWriteObjectBody(final Object object, final Collection<String> ignored) throws IllegalAccessException, InvocationTargetException {
+    private void doWriteObjectBody(final Object object, final Collection<String> ignored, String jsonPointer)
+            throws IllegalAccessException, InvocationTargetException {
+
+        if (jsonPointer != null) {
+            jsonPointers.put(object, jsonPointer);
+        }
+
         final Class<?> objectClass = object.getClass();
         final Mappings.ClassMapping classMapping = mappings.findOrCreateClassMapping(objectClass);
         if (classMapping == null) {
@@ -236,7 +246,7 @@ public class MappingGeneratorImpl implements MappingGenerator {
             return;
         }
         if (classMapping.adapter != null) {
-            doWriteObjectBody(classMapping.adapter.to(object), ignored);
+            doWriteObjectBody(classMapping.adapter.to(object), ignored, null);
             return;
         }
 
@@ -266,13 +276,23 @@ public class MappingGeneratorImpl implements MappingGenerator {
 
             final Object val = getter.converter == null ? value : getter.converter.from(value);
 
-            writeValue(val.getClass(),
-                    getter.primitive, getter.array,
-                    getter.collection, getter.map,
-                    getter.itemConverter,
-                    getterEntry.getKey(),
-                    val, getter.objectConverter,
-                    getter.ignoreNested);
+            String valJsonPointer = jsonPointers.get(val);
+            if (valJsonPointer != null) {
+                // write the JsonPointer instead
+                generator.write(getterEntry.getKey(), valJsonPointer);
+            } else {
+                writeValue(val.getClass(),
+                        getter.primitive,
+                        getter.array,
+                        getter.collection,
+                        getter.map,
+                        getter.itemConverter,
+                        getterEntry.getKey(),
+                        val,
+                        getter.objectConverter,
+                        getter.ignoreNested,
+                        nestJsonPointer(jsonPointer, getterEntry.getKey()));
+            }
         }
 
         // @JohnzonAny doesn't respect comparator since it is a map and not purely in the model we append it after and
@@ -285,13 +305,27 @@ public class MappingGeneratorImpl implements MappingGenerator {
         }
     }
 
+    private String nestJsonPointer(String jsonPointer, String attribName) {
+        if (jsonPointer == null) {
+            return null;
+        }
+        if (jsonPointer.length() == 1) {
+            // the root element
+            return jsonPointer + attribName;
+        } else {
+            return jsonPointer + "/" + JsonPointerUtil.encode(attribName);
+        }
+    }
+
     private void writeValue(final Class<?> type,
                             final boolean primitive, final boolean array,
                             final boolean collection, final boolean map,
                             final Adapter itemConverter,
                             final String key, final Object value,
                             final ObjectConverter.Writer objectConverter,
-                            final Collection<String> ignoredProperties) throws InvocationTargetException, IllegalAccessException {
+                            final Collection<String> ignoredProperties,
+                            final String jsonPointer)
+            throws InvocationTargetException, IllegalAccessException {
         if (config.getSerializeValueFilter().shouldIgnore(key, value)) {
             return;
         }
@@ -340,7 +374,7 @@ public class MappingGeneratorImpl implements MappingGenerator {
                 if (writePrimitives(key, adapted.getClass(), adapted)) {
                     return;
                 }
-                writeValue(String.class, true, false, false, false, null, key, adapted, null, ignoredProperties);
+                writeValue(String.class, true, false, false, false, null, key, adapted, null, ignoredProperties, null);
                 return;
             } else {
 
@@ -360,7 +394,7 @@ public class MappingGeneratorImpl implements MappingGenerator {
                 return;
             }
             generator.writeStartObject(key);
-            doWriteObjectBody(value, ignoredProperties);
+            doWriteObjectBody(value, ignoredProperties, jsonPointer);
             generator.writeEnd();
         }
     }

http://git-wip-us.apache.org/repos/asf/johnzon/blob/0091f201/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/CircularExceptionTest.java
----------------------------------------------------------------------
diff --git a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/CircularExceptionTest.java b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/CircularExceptionTest.java
index 846e469..17fe5f7 100644
--- a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/CircularExceptionTest.java
+++ b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/CircularExceptionTest.java
@@ -18,6 +18,7 @@
  */
 package org.apache.johnzon.mapper;
 
+import org.junit.Assert;
 import org.junit.Test;
 
 import static org.junit.Assert.assertTrue;
@@ -32,4 +33,40 @@ public class CircularExceptionTest {
         assertTrue(serialized.contains("\"detailMessage\":\"circular\""));
         assertTrue(serialized.contains("\"stackTrace\":[{"));
     }
+
+    @Test
+    public void testCyclicPerson() {
+        Person john = new Person("John");
+        Person marry = new Person("Marry");
+
+        john.setMarriedTo(marry);
+        marry.setMarriedTo(john);
+
+        String ser = new MapperBuilder().setAccessModeName("field").build().writeObjectAsString(john);
+        Assert.assertNotNull(ser);
+        assertTrue(ser.contains("\"name\":\"John\""));
+        assertTrue(ser.contains("\"marriedTo\":\"/\""));
+        assertTrue(ser.contains("\"name\":\"Marry\""));
+    }
+
+    public static class Person {
+        private final String name;
+        private Person marriedTo;
+
+        public Person(String name) {
+            this.name = name;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public Person getMarriedTo() {
+            return marriedTo;
+        }
+
+        public void setMarriedTo(Person marriedTo) {
+            this.marriedTo = marriedTo;
+        }
+    }
 }