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 <[email protected]> Authored: Tue Apr 18 21:17:52 2017 +0200 Committer: rmannibucau <[email protected]> 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; + } +}
