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/08/11 19:05:07 UTC
[johnzon] branch master updated: JOHNZON-242 ensure arrays of
objects work with 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 7680b84 JOHNZON-242 ensure arrays of objects work with serializers
7680b84 is described below
commit 7680b84a774814f92dfe46d3b8a7c908ba77680b
Author: Romain Manni-Bucau <rm...@apache.org>
AuthorDate: Sun Aug 11 21:04:51 2019 +0200
JOHNZON-242 ensure arrays of objects work with serializers
---
.../org/apache/johnzon/jsonb/JsonbAccessMode.java | 28 +-
.../org/apache/johnzon/jsonb/SerializerTest.java | 284 +++++++++++++++++++++
.../johnzon/mapper/DynamicMappingGenerator.java | 133 +++++++---
.../johnzon/mapper/MappingGeneratorImpl.java | 24 +-
.../org/apache/johnzon/mapper/ObjectConverter.java | 14 +
5 files changed, 438 insertions(+), 45 deletions(-)
diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java
index d430960..0fa1ef0 100644
--- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java
+++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java
@@ -83,6 +83,7 @@ import javax.json.bind.config.PropertyVisibilityStrategy;
import javax.json.bind.serializer.JsonbDeserializer;
import javax.json.bind.serializer.JsonbSerializer;
import javax.json.spi.JsonProvider;
+import javax.json.stream.JsonGenerator;
import javax.json.stream.JsonParserFactory;
import org.apache.johnzon.core.Types;
@@ -102,6 +103,7 @@ import org.apache.johnzon.mapper.Converter;
import org.apache.johnzon.mapper.JohnzonAny;
import org.apache.johnzon.mapper.JohnzonConverter;
import org.apache.johnzon.mapper.MapperConverter;
+import org.apache.johnzon.mapper.MappingGenerator;
import org.apache.johnzon.mapper.MappingParser;
import org.apache.johnzon.mapper.ObjectConverter;
import org.apache.johnzon.mapper.TypeAwareAdapter;
@@ -833,16 +835,23 @@ public class JsonbAccessMode implements AccessMode, Closeable {
final Class<?> mappedType = types.findParamType(pt, JsonbDeserializer.class);
toRelease.add(instance);
final JsonBuilderFactory builderFactoryInstance = builderFactory.get();
+ final Type[] arguments = types.findParameterizedType(value, JsonbDeserializer.class).getActualTypeArguments();
+ final boolean global = arguments.length == 1 && arguments[0] != null && arguments[0].equals(annotationHolder.getType());
reader = new ObjectConverter.Reader() {
private final ConcurrentMap<Type, BiFunction<JsonValue, MappingParser, Object>> impl =
new ConcurrentHashMap<>();
@Override
+ public boolean isGlobal() {
+ return global;
+ }
+
+ @Override
public Object fromJson(final JsonValue value,
final Type targetType,
final MappingParser parser) {
final JsonbDeserializer jsonbDeserializer = instance.getValue();
- if (targetType == mappedType) { // fast test and matches most cases
+ if (global || targetType == mappedType) { // fast test and matches most cases
return mapItem(value, targetType, parser, jsonbDeserializer);
}
@@ -921,8 +930,21 @@ public class JsonbAccessMode implements AccessMode, Closeable {
final Class<? extends JsonbSerializer> value = serializer.value();
final JohnzonAdapterFactory.Instance<? extends JsonbSerializer> instance = newInstance(value);
toRelease.add(instance);
- writer = (instance1, jsonbGenerator) ->
- instance.getValue().serialize(instance1, jsonbGenerator.getJsonGenerator(), new JohnzonSerializationContext(jsonbGenerator));
+ final Type[] arguments = types.findParameterizedType(value, JsonbSerializer.class).getActualTypeArguments();
+ final boolean global = arguments.length == 1 && arguments[0] != null && arguments[0].equals(reader.getType());
+ final JsonbSerializer jsonbSerializer = instance.getValue();
+ writer = new ObjectConverter.Writer() {
+ @Override
+ public void writeJson(final Object instance, final MappingGenerator jsonbGenerator) {
+ final JsonGenerator generator = jsonbGenerator.getJsonGenerator();
+ jsonbSerializer.serialize(instance, generator, new JohnzonSerializationContext(jsonbGenerator));
+ }
+
+ @Override
+ public boolean isGlobal() {
+ return global;
+ }
+ };
} else if (johnzonConverter != null) {
try {
MapperConverter mapperConverter = johnzonConverter.value().newInstance();
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 7945835..e535b54 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
@@ -28,10 +28,12 @@ import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.UUID;
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
+import javax.json.bind.JsonbConfig;
import javax.json.bind.annotation.JsonbTransient;
import javax.json.bind.annotation.JsonbTypeDeserializer;
import javax.json.bind.annotation.JsonbTypeSerializer;
@@ -171,6 +173,288 @@ public class SerializerTest {
jsonb.close();
}
+ @Test
+ public void fromConfig() throws Exception {
+ try (final Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()
+ .withSerializers(new AnimalSerializer()).withDeserializers(new AnimalDeserializer()))) {
+ final Animals animals = new Animals();
+ animals.animals.add(new Cat(5, "Garfield", 10.5f, true, true));
+ animals.animals.add(new Dog(3, "Milo", 5.5f, false, true));
+ animals.animals.add(new Animal(6, "Tweety", 0.5f, false));
+
+ final String jsonString = jsonb.toJson(animals);
+ assertEquals("{\"animals\":[" +
+ "{\"type\":\"cat\",\"cuddly\":true,\"age\":5,\"furry\":true,\"name\":\"Garfield\",\"weight\":10.5}," +
+ "{\"type\":\"dog\",\"barking\":true,\"age\":3,\"furry\":false,\"name\":\"Milo\",\"weight\":5.5}," +
+ "{\"type\":\"animal\",\"age\":6,\"furry\":false,\"name\":\"Tweety\",\"weight\":0.5}]}",
+ jsonString);
+
+ final Animals deserialized = jsonb.fromJson("{ \"animals\" : [ "
+ + "{ \"type\" : \"cat\", \"cuddly\" : true, \"age\" : 5, \"furry\" : true, \"name\" : \"Garfield\" , \"weight\" : 10.5}, "
+ + "{ \"type\" : \"dog\", \"barking\" : true, \"age\" : 3, \"furry\" : false, \"name\" : \"Milo\", \"weight\" : 5.5}, "
+ + "{ \"type\" : \"animal\", \"age\" : 6, \"furry\" : false, \"name\" : \"Tweety\", \"weight\" : 0.5}"
+ + " ] }", Animals.class);
+ assertEquals(animals.animals, deserialized.animals);
+ }
+ }
+
+ @Test
+ public void fromAnnotation() {
+ final AnimalsAnnotation animals = new AnimalsAnnotation();
+ animals.animals.add(new Cat(5, "Garfield", 10.5f, true, true));
+ animals.animals.add(new Dog(3, "Milo", 5.5f, false, true));
+ animals.animals.add(new Animal(6, "Tweety", 0.5f, false));
+
+ final String jsonString = jsonb.toJson(animals);
+ assertEquals("{\"animals\":[" +
+ "{\"type\":\"cat\",\"cuddly\":true,\"age\":5,\"furry\":true,\"name\":\"Garfield\",\"weight\":10.5}," +
+ "{\"type\":\"dog\",\"barking\":true,\"age\":3,\"furry\":false,\"name\":\"Milo\",\"weight\":5.5}," +
+ "{\"type\":\"animal\",\"age\":6,\"furry\":false,\"name\":\"Tweety\",\"weight\":0.5}]}", jsonString);
+
+ final AnimalsAnnotation deserialized = jsonb.fromJson("{ \"animals\" : [ "
+ + "{ \"type\" : \"cat\", \"cuddly\" : true, \"age\" : 5, \"furry\" : true, \"name\" : \"Garfield\" , \"weight\" : 10.5}, "
+ + "{ \"type\" : \"dog\", \"barking\" : true, \"age\" : 3, \"furry\" : false, \"name\" : \"Milo\", \"weight\" : 5.5}, "
+ + "{ \"type\" : \"animal\", \"age\" : 6, \"furry\" : false, \"name\" : \"Tweety\", \"weight\" : 0.5}"
+ + " ] }", AnimalsAnnotation.class);
+ assertEquals(animals.animals, deserialized.animals);
+ }
+
+ public static class Animal {
+ public int age;
+ public String name;
+ public float weight;
+ public boolean furry;
+
+ public Animal() {
+ // no-op
+ }
+
+ public Animal(int age, String name, float weight, boolean furry) {
+ this.age = age;
+ this.name = name;
+ this.weight = weight;
+ this.furry = furry;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final Animal animal = (Animal) o;
+ return age == animal.age &&
+ Float.compare(animal.weight, weight) == 0 &&
+ furry == animal.furry &&
+ Objects.equals(name, animal.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(age, name, weight, furry);
+ }
+ }
+
+ public static class Dog extends Animal {
+ public boolean barking;
+
+ public Dog() {
+ // no-op
+ }
+
+ public Dog(int age, String name, float weight, boolean furry,
+ boolean barking) {
+ super(age, name, weight, furry);
+ this.barking = barking;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+ final Dog dog = (Dog) o;
+ return barking == dog.barking;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), barking);
+ }
+ }
+
+ public static class Cat extends Animal {
+ public boolean cuddly;
+
+ public Cat() {
+ // no-op
+ }
+
+ public Cat(int age, String name, float weight, boolean furry,
+ boolean cuddly) {
+ super(age, name, weight, furry);
+ this.cuddly = cuddly;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+ final Cat cat = (Cat) o;
+ return cuddly == cat.cuddly;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), cuddly);
+ }
+ }
+
+ public static class AnimalListDeserializer implements JsonbDeserializer<List<Animal>> {
+ private final AnimalDeserializer animalDeserializer = new AnimalDeserializer();
+
+ @Override
+ public List<Animal> deserialize(final JsonParser parser,
+ final DeserializationContext ctx,
+ final Type type) {
+ final List<Animal> animals = new ArrayList<>();
+ while (parser.hasNext()) {
+ JsonParser.Event event = parser.next();
+ while (event == JsonParser.Event.START_OBJECT) {
+ animals.add(animalDeserializer.deserialize(parser, ctx, type));
+ event = parser.next();
+ }
+ }
+ return animals;
+ }
+ }
+
+ public static class AnimalListSerializer implements JsonbSerializer<List<Animal>> {
+ private final AnimalSerializer animalSerializer = new AnimalSerializer();
+
+ @Override
+ public void serialize(final List<Animal> animals, final JsonGenerator gen,
+ final SerializationContext ctx) {
+ gen.writeStartArray();
+ for (final Animal animal : animals) {
+ animalSerializer.serialize(animal, gen, ctx);
+ }
+ gen.writeEnd();
+ }
+ }
+
+ public static class AnimalsAnnotation {
+ @JsonbTypeSerializer(AnimalListSerializer.class)
+ @JsonbTypeDeserializer(AnimalListDeserializer.class)
+ public List<Animal> animals = new ArrayList<>();
+ }
+
+ public static class Animals {
+ public List<Animal> animals = new ArrayList<>();
+ }
+
+ public static class AnimalDeserializer implements JsonbDeserializer<Animal> {
+ @Override
+ public Animal deserialize(final JsonParser jsonParser,
+ final DeserializationContext deserializationContext,
+ final Type type) {
+ Animal animal = null;
+ while (jsonParser.hasNext()) {
+ JsonParser.Event event = jsonParser.next();
+ if (event == JsonParser.Event.START_OBJECT) {
+ continue;
+ }
+ if (event == JsonParser.Event.END_OBJECT) {
+ break;
+ }
+ if (event == JsonParser.Event.KEY_NAME) {
+ switch (jsonParser.getString()) {
+ case "type":
+ jsonParser.next();
+ switch (jsonParser.getString()) {
+ case "cat":
+ animal = new Cat();
+ break;
+ case "dog":
+ animal = new Dog();
+ break;
+ default:
+ animal = new Animal();
+ }
+ break;
+ case "name":
+ jsonParser.next();
+ animal.name = jsonParser.getString();
+ break;
+ case "age":
+ jsonParser.next();
+ animal.age = jsonParser.getInt();
+ break;
+ case "furry":
+ event = jsonParser.next();
+ animal.furry = event == JsonParser.Event.VALUE_TRUE;
+ break;
+ case "weight":
+ jsonParser.next();
+ animal.weight = jsonParser.getBigDecimal().floatValue();
+ break;
+ case "cuddly":
+ event = jsonParser.next();
+ ((Cat) animal).cuddly = event == JsonParser.Event.VALUE_TRUE;
+ break;
+ case "barking":
+ event = jsonParser.next();
+ ((Dog) animal).barking = event == JsonParser.Event.VALUE_TRUE;
+ break;
+ default:
+ }
+ }
+ }
+ return animal;
+ }
+ }
+
+ public static class AnimalSerializer implements JsonbSerializer<Animal> {
+ @Override
+ public void serialize(final Animal animal, final JsonGenerator jsonGenerator,
+ final SerializationContext serializationContext) {
+ if (animal != null) {
+ jsonGenerator.writeStartObject();
+ if (Cat.class.isAssignableFrom(animal.getClass())) {
+ jsonGenerator.write("type", "cat");
+ jsonGenerator.write("cuddly", ((Cat) animal).cuddly);
+ } else if (Dog.class.isAssignableFrom(animal.getClass())) {
+ jsonGenerator.write("type", "dog");
+ jsonGenerator.write("barking", ((Dog) animal).barking);
+ } else {
+ jsonGenerator.write("type", "animal");
+ }
+ jsonGenerator.write("age", animal.age);
+ jsonGenerator.write("furry", animal.furry);
+ jsonGenerator.write("name", animal.name);
+ jsonGenerator.write("weight", animal.weight);
+ jsonGenerator.writeEnd();
+ } else {
+ serializationContext.serialize(null, jsonGenerator);
+ }
+ }
+ }
+
public static class StringHolder implements Holder<String> {
private String instance = "Test";
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 5e7c5c3..bbbb8f0 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
@@ -66,14 +66,15 @@ public class DynamicMappingGenerator implements MappingGenerator {
}
public void flushIfNeeded() {
- if (this.generator.state == WritingState.WROTE_START_OBJECT) {
+ if (this.generator.state == WritingState.WROTE_START) {
writeEnd.run();
this.generator.state = WritingState.NONE;
}
}
private enum WritingState {
- NONE, WROTE_START_OBJECT,
+ NONE,
+ WROTE_START,
DONT_WRITE_END
}
@@ -81,7 +82,8 @@ public class DynamicMappingGenerator implements MappingGenerator {
private final JsonGenerator delegate;
private final Runnable writeStart;
private final String keyIfNoObject;
- private WritingState state = WritingState.NONE;
+ private WritingState state = WritingState.NONE; // todo: we need a stack (linkedlist) here to be accurate
+ private int nested = 0;
private InObjectOrPrimitiveJsonGenerator(final JsonGenerator generator, final Runnable writeStart,
final String keyName) {
@@ -91,184 +93,239 @@ public class DynamicMappingGenerator implements MappingGenerator {
}
private void ensureStart() {
- if (state == WritingState.WROTE_START_OBJECT) {
+ if (state != WritingState.NONE) {
return;
}
writeStart.run();
- state = WritingState.WROTE_START_OBJECT;
+ state = WritingState.WROTE_START;
}
@Override
public JsonGenerator writeStartObject() {
- // return delegate.writeStartObject();
+ if (state == WritingState.NONE) {
+ ensureStart();
+ } else {
+ nested++;
+ delegate.writeStartObject();
+ }
return this;
}
@Override
public JsonGenerator writeStartObject(final String name) {
+ if (state != WritingState.NONE) {
+ nested++;
+ }
ensureStart();
- return delegate.writeStartObject(name);
+ delegate.writeStartObject(name);
+ return this;
}
@Override
public JsonGenerator writeStartArray() {
+ if (state != WritingState.NONE) {
+ nested++;
+ }
if (keyIfNoObject != null && state == WritingState.NONE) {
state = WritingState.DONT_WRITE_END; // skip writeEnd since the impl will do it
return delegate.writeStartArray(keyIfNoObject);
+ } else if (state == WritingState.NONE) {
+ ensureStart();
+ return this;
}
- return delegate.writeStartArray();
+ delegate.writeStartArray();
+ return this;
}
@Override
public JsonGenerator writeStartArray(final String name) {
+ if (state != WritingState.NONE) {
+ nested++;
+ }
ensureStart();
- return delegate.writeStartArray(name);
+ delegate.writeStartArray(name);
+ return this;
}
@Override
public JsonGenerator writeKey(final String name) {
ensureStart();
- return delegate.writeKey(name);
+ delegate.writeKey(name);
+ return this;
}
@Override
public JsonGenerator write(final String name, final JsonValue value) {
ensureStart();
- return delegate.write(name, value);
+ delegate.write(name, value);
+ return this;
}
@Override
public JsonGenerator write(final String name, final String value) {
ensureStart();
- return delegate.write(name, value);
+ delegate.write(name, value);
+ return this;
}
@Override
public JsonGenerator write(final String name, final BigInteger value) {
ensureStart();
- return delegate.write(name, value);
+ delegate.write(name, value);
+ return this;
}
@Override
public JsonGenerator write(final String name, final BigDecimal value) {
ensureStart();
- return delegate.write(name, value);
+ delegate.write(name, value);
+ return this;
}
@Override
public JsonGenerator write(final String name, final int value) {
ensureStart();
- return delegate.write(name, value);
+ delegate.write(name, value);
+ return this;
}
@Override
public JsonGenerator write(final String name, final long value) {
ensureStart();
- return delegate.write(name, value);
+ delegate.write(name, value);
+ return this;
}
@Override
public JsonGenerator write(final String name, final double value) {
ensureStart();
- return delegate.write(name, value);
+ delegate.write(name, value);
+ return this;
}
@Override
public JsonGenerator write(final String name, final boolean value) {
ensureStart();
- return delegate.write(name, value);
+ delegate.write(name, value);
+ return this;
}
@Override
public JsonGenerator writeNull(final String name) {
ensureStart();
- return delegate.writeNull(name);
+ delegate.writeNull(name);
+ return this;
}
@Override
public JsonGenerator writeEnd() {
- return delegate.writeEnd();
+ if (nested == 0 && state == WritingState.WROTE_START) {
+ state = WritingState.NONE;
+ }
+ if (nested > 0) {
+ nested--;
+ }
+ delegate.writeEnd();
+ return this;
}
@Override
public JsonGenerator write(final JsonValue value) {
if (isWritingPrimitive()) {
state = WritingState.DONT_WRITE_END;
- return delegate.write(keyIfNoObject, value);
+ delegate.write(keyIfNoObject, value);
+ return this;
}
- return delegate.write(value);
+ delegate.write(value);
+ return this;
}
@Override
public JsonGenerator write(final String value) {
if (isWritingPrimitive()) {
state = WritingState.DONT_WRITE_END;
- return delegate.write(keyIfNoObject, value);
+ delegate.write(keyIfNoObject, value);
+ return this;
}
- return delegate.write(value);
+ delegate.write(value);
+ return this;
}
@Override
public JsonGenerator write(final BigDecimal value) {
if (isWritingPrimitive()) {
state = WritingState.DONT_WRITE_END;
- return delegate.write(keyIfNoObject, value);
+ delegate.write(keyIfNoObject, value);
+ return this;
}
- return delegate.write(value);
+ delegate.write(value);
+ return this;
}
@Override
public JsonGenerator write(final BigInteger value) {
if (isWritingPrimitive()) {
state = WritingState.DONT_WRITE_END;
- return delegate.write(keyIfNoObject, value);
+ delegate.write(keyIfNoObject, value);
+ return this;
}
- return delegate.write(value);
+ delegate.write(value);
+ return this;
}
@Override
public JsonGenerator write(final int value) {
if (isWritingPrimitive()) {
state = WritingState.DONT_WRITE_END;
- return delegate.write(keyIfNoObject, value);
+ delegate.write(keyIfNoObject, value);
+ return this;
}
- return delegate.write(value);
+ delegate.write(value);
+ return this;
}
@Override
public JsonGenerator write(final long value) {
if (isWritingPrimitive()) {
state = WritingState.DONT_WRITE_END;
- return delegate.write(keyIfNoObject, value);
+ delegate.write(keyIfNoObject, value);
+ return this;
}
- return delegate.write(value);
+ delegate.write(value);
+ return this;
}
@Override
public JsonGenerator write(final double value) {
if (isWritingPrimitive()) {
state = WritingState.DONT_WRITE_END;
- return delegate.write(keyIfNoObject, value);
+ delegate.write(keyIfNoObject, value);
+ return this;
}
- return delegate.write(value);
+ delegate.write(value);
+ return this;
}
@Override
public JsonGenerator write(boolean value) {
if (isWritingPrimitive()) {
state = WritingState.DONT_WRITE_END;
- return delegate.write(keyIfNoObject, value);
+ delegate.write(keyIfNoObject, value);
+ return this;
}
- return delegate.write(value);
+ delegate.write(value);
+ return this;
}
@Override
public JsonGenerator writeNull() {
if (isWritingPrimitive()) {
state = WritingState.DONT_WRITE_END;
- return delegate.writeNull(keyIfNoObject);
+ delegate.writeNull(keyIfNoObject);
+ return this;
}
- return delegate.writeNull();
+ delegate.writeNull();
+ return this;
}
private boolean isWritingPrimitive() {
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 1d6119d..ca1c049 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
@@ -19,6 +19,7 @@
package org.apache.johnzon.mapper;
import static java.util.Collections.emptyList;
+import static java.util.stream.Collectors.toList;
import org.apache.johnzon.mapper.internal.JsonPointerTracker;
import org.apache.johnzon.mapper.util.ArrayUtil;
@@ -33,8 +34,12 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
+import java.util.Spliterator;
+import java.util.Spliterators;
import java.util.stream.BaseStream;
+import java.util.stream.StreamSupport;
public class MappingGeneratorImpl implements MappingGenerator {
private final MapperConfig config;
@@ -401,7 +406,7 @@ public class MappingGeneratorImpl implements MappingGenerator {
writeArray(type, itemConverter, key, value, ignoredProperties, jsonPointer);
} else if ((!dynamic && collection) || (dynamic && Iterable.class.isAssignableFrom(type))) {
writeIterator(itemConverter, key, objectConverter, ignoredProperties, jsonPointer, generator,
- Iterable.class.cast(value).iterator());
+ Iterable.class.cast(value).iterator(), value);
} else if ((!dynamic && map) || (dynamic && Map.class.isAssignableFrom(type))) {
generator.writeStartObject(key);
writeMapBody((Map<?, ?>) value, itemConverter);
@@ -417,10 +422,10 @@ public class MappingGeneratorImpl implements MappingGenerator {
}
} else if (BaseStream.class.isAssignableFrom(type)) {
writeIterator(itemConverter, key, objectConverter, ignoredProperties, jsonPointer, generator,
- BaseStream.class.cast(value).iterator());
+ BaseStream.class.cast(value).iterator(), value);
} else if (Iterator.class.isAssignableFrom(type)) {
writeIterator(itemConverter, key, objectConverter, ignoredProperties, jsonPointer, generator,
- Iterator.class.cast(value));
+ Iterator.class.cast(value), value);
} else {
if (objectConverter != null) {
final DynamicMappingGenerator dynamicMappingGenerator = new DynamicMappingGenerator(this,
@@ -466,7 +471,18 @@ public class MappingGeneratorImpl implements MappingGenerator {
final Collection<String> ignoredProperties,
final JsonPointerTracker jsonPointer,
final JsonGenerator generator,
- final Iterator<?> iterator) {
+ final Iterator<?> iterator,
+ final Object originalValue) {
+ if (objectConverter != null && objectConverter.isGlobal()) {
+ final List<Object> list = List.class.isInstance(originalValue) ?
+ List.class.cast(originalValue) :
+ StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.IMMUTABLE), false)
+ .collect(toList());
+ objectConverter.writeJson(list, new DynamicMappingGenerator(
+ this, generator::writeStartArray, generator::writeEnd, key));
+ return;
+ }
+
int i = 0;
generator.writeStartArray(key);
while (iterator.hasNext()) {
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/ObjectConverter.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/ObjectConverter.java
index a468c73..673585d 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/ObjectConverter.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/ObjectConverter.java
@@ -37,12 +37,26 @@ public final class ObjectConverter {
public interface Writer<T> extends MapperConverter {
void writeJson(T instance, MappingGenerator jsonbGenerator);
+
+ // returns true if it is for containers - if any - and not each container item (ex: list)
+ default boolean isGlobal() {
+ return false;
+ }
}
public interface Reader<T> extends MapperConverter {
T fromJson(JsonValue jsonValue, Type targetType, MappingParser parser);
+
+ // returns true if it is for containers - if any - and not each container item (ex: list)
+ default boolean isGlobal() {
+ return false;
+ }
}
public interface Codec<T> extends ObjectConverter.Writer<T>, ObjectConverter.Reader<T> {
+ @Override
+ default boolean isGlobal() {
+ return false;
+ }
}
}