-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added java.lang.Iterable support #1389
Conversation
I'm new at this am I suppose to download or this |
I'm new at this am I suppose to download or this |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe there should be a warning somewhere because Iterable
is not guaranteed to support calling iterator()
multiple times (see DirectoryStream
), so it might be surprising why after serializing an object, it suddenly behaves differently.
Additionally doesn't com.google.gson.internal.bind.CollectionTypeAdapterFactory.Adapter
have to be adjusted as well by this pull request?
gson/src/main/java/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java
Show resolved
Hide resolved
gson/src/main/java/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java
Show resolved
Hide resolved
I agree to some extent, but that interface has been added since 1.7 whereas Gson is still strongly 1.6-oriented, so even if that stream behaves surprisingly, let's just consider that implementation (and those like that) poorly designed violating the LSP. Why: calling the |
I was thinking of this too, and I'm not sure but it's probably better to leave the current "collection-only" implementation as is and only mix the iterable support into it. The collection type adapter factory is declared public, and it might be used externally, not via Gson indirectly. |
Ah nevermind, I am rather certain that you definitely have to adjust |
Hm, a good point. I definitely missed that it and going to check it again. Thanks for pointing that out! |
@Marcono1234 I believe this is the biggest "why" why Gson does not support it out of box due to the symmetry issues and I feel ashamed to be blind not seeing it earlier. |
From 86a605c4b33a9fb18ff76b9101b25d65e7e7f3d1 Mon Sep 17 00:00:00 2001
From: Lyubomyr Shaydariv <[email protected]>
Date: Sun, 23 Sep 2018 12:11:24 +0300
Subject: [PATCH] Added java.lang.Iterable support
---
gson/src/main/java/com/google/gson/Gson.java | 1 +
.../com/google/gson/internal/$Gson$Types.java | 16 ++++++
.../gson/internal/ConstructorConstructor.java | 8 +++
.../bind/CollectionTypeAdapterFactory.java | 32 +++++++++---
.../gson/functional/CollectionTest.java | 49 +++++++++++++++++++
5 files changed, 100 insertions(+), 6 deletions(-)
diff --git a/gson/src/main/java/com/google/gson/Gson.java b/gson/src/main/java/com/google/gson/Gson.java
index 27f3ee92..30498ffc 100644
--- a/gson/src/main/java/com/google/gson/Gson.java
+++ b/gson/src/main/java/com/google/gson/Gson.java
@@ -270,6 +270,7 @@ public final class Gson {
// type adapters for composite and user-defined types
factories.add(new CollectionTypeAdapterFactory(constructorConstructor));
+ factories.add(new CollectionTypeAdapterFactory(constructorConstructor, true));
factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization));
this.jsonAdapterFactory = new JsonAdapterAnnotationTypeAdapterFactory(constructorConstructor);
factories.add(jsonAdapterFactory);
diff --git a/gson/src/main/java/com/google/gson/internal/$Gson$Types.java b/gson/src/main/java/com/google/gson/internal/$Gson$Types.java
index adea605f..5a38adfa 100644
--- a/gson/src/main/java/com/google/gson/internal/$Gson$Types.java
+++ b/gson/src/main/java/com/google/gson/internal/$Gson$Types.java
@@ -294,6 +294,22 @@ public final class $Gson$Types {
: ((Class<?>) array).getComponentType();
}
+ /**
+ * Returns the element type of this iterable type.
+ * @throws IllegalArgumentException if this type is not an iterable.
+ */
+ public static Type getIterableElementType(Type context, Class<?> contextRawType) {
+ Type iterableType = getSupertype(context, contextRawType, Iterable.class);
+
+ if (iterableType instanceof WildcardType) {
+ iterableType = ((WildcardType)iterableType).getUpperBounds()[0];
+ }
+ if (iterableType instanceof ParameterizedType) {
+ return ((ParameterizedType) iterableType).getActualTypeArguments()[0];
+ }
+ return Object.class;
+ }
+
/**
* Returns the element type of this collection type.
* @throws IllegalArgumentException if this type is not a collection.
diff --git a/gson/src/main/java/com/google/gson/internal/ConstructorConstructor.java b/gson/src/main/java/com/google/gson/internal/ConstructorConstructor.java
index 5fab4601..8d3aedd6 100644
--- a/gson/src/main/java/com/google/gson/internal/ConstructorConstructor.java
+++ b/gson/src/main/java/com/google/gson/internal/ConstructorConstructor.java
@@ -177,6 +177,14 @@ public final class ConstructorConstructor {
}
}
+ if (Iterable.class.isAssignableFrom(rawType)) {
+ return new ObjectConstructor<T>() {
+ @Override public T construct() {
+ return (T) new ArrayList<Object>();
+ }
+ };
+ }
+
if (Map.class.isAssignableFrom(rawType)) {
if (ConcurrentNavigableMap.class.isAssignableFrom(rawType)) {
return new ObjectConstructor<T>() {
diff --git a/gson/src/main/java/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java b/gson/src/main/java/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java
index 1d57844a..5d79b56d 100644
--- a/gson/src/main/java/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java
+++ b/gson/src/main/java/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java
@@ -35,9 +35,15 @@ import java.util.Collection;
*/
public final class CollectionTypeAdapterFactory implements TypeAdapterFactory {
private final ConstructorConstructor constructorConstructor;
+ private final boolean supportIterable;
public CollectionTypeAdapterFactory(ConstructorConstructor constructorConstructor) {
+ this(constructorConstructor, false);
+ }
+
+ public CollectionTypeAdapterFactory(ConstructorConstructor constructorConstructor, boolean supportIterable) {
this.constructorConstructor = constructorConstructor;
+ this.supportIterable = supportIterable;
}
@Override
@@ -45,11 +51,11 @@ public final class CollectionTypeAdapterFactory implements TypeAdapterFactory {
Type type = typeToken.getType();
Class<? super T> rawType = typeToken.getRawType();
- if (!Collection.class.isAssignableFrom(rawType)) {
+ if (!isRawTypeSupported(rawType)) {
return null;
}
- Type elementType = $Gson$Types.getCollectionElementType(type, rawType);
+ Type elementType = getElementType(type, rawType);
TypeAdapter<?> elementTypeAdapter = gson.getAdapter(TypeToken.get(elementType));
ObjectConstructor<T> constructor = constructorConstructor.get(typeToken);
@@ -58,7 +64,21 @@ public final class CollectionTypeAdapterFactory implements TypeAdapterFactory {
return result;
}
- private static final class Adapter<E> extends TypeAdapter<Collection<E>> {
+ private boolean isRawTypeSupported(Class<?> rawType) {
+ if (supportIterable) {
+ return Iterable.class.isAssignableFrom(rawType);
+ }
+ return Collection.class.isAssignableFrom(rawType);
+ }
+
+ private Type getElementType(Type type, Class<?> rawType) {
+ if (supportIterable) {
+ return $Gson$Types.getIterableElementType(type, rawType);
+ }
+ return $Gson$Types.getCollectionElementType(type, rawType);
+ }
+
+ private static final class Adapter<E> extends TypeAdapter<Iterable<E>> {
private final TypeAdapter<E> elementTypeAdapter;
private final ObjectConstructor<? extends Collection<E>> constructor;
@@ -86,14 +106,14 @@ public final class CollectionTypeAdapterFactory implements TypeAdapterFactory {
return collection;
}
- @Override public void write(JsonWriter out, Collection<E> collection) throws IOException {
- if (collection == null) {
+ @Override public void write(JsonWriter out, Iterable<E> iterable) throws IOException {
+ if (iterable == null) {
out.nullValue();
return;
}
out.beginArray();
- for (E element : collection) {
+ for (E element : iterable) {
elementTypeAdapter.write(out, element);
}
out.endArray();
diff --git a/gson/src/test/java/com/google/gson/functional/CollectionTest.java b/gson/src/test/java/com/google/gson/functional/CollectionTest.java
index 8aa36e21..85bef1b2 100644
--- a/gson/src/test/java/com/google/gson/functional/CollectionTest.java
+++ b/gson/src/test/java/com/google/gson/functional/CollectionTest.java
@@ -25,6 +25,7 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.NoSuchElementException;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Set;
@@ -59,6 +60,27 @@ public class CollectionTest extends TestCase {
gson = new Gson();
}
+ public void testCollectionSubstitutesIterable() {
+ BagOfPrimitives foo = new BagOfPrimitives(1L, 2, true, "foo");
+ BagOfPrimitives bar = new BagOfPrimitives(3L, 4, false, "bar");
+ Iterable<BagOfPrimitives> before = Arrays.asList(foo, bar);
+ Type iterableType = new TypeToken<Iterable<BagOfPrimitives>>() {}.getType();
+ Type collectionType = new TypeToken<Collection<BagOfPrimitives>>() {}.getType();
+ String actualJson = gson.toJson(before, iterableType);
+ String expectedJson = gson.toJson(before, collectionType);
+ assertEquals(expectedJson, actualJson);
+ Iterable<BagOfPrimitives> after = gson.fromJson(actualJson, iterableType);
+ assertTrue(after instanceof Collection);
+ assertEquals(before, after);
+ }
+
+ public void testIterableThatIsNotACollection() {
+ Iterable<Character> charsBefore = new Chars('0', '9');
+ String json = gson.toJson(charsBefore, Chars.class);
+ assertEquals("[\"0\",\"1\",\"2\",\"3\",\"4\",\"5\",\"6\",\"7\",\"8\",\"9\"]", json);
+ Chars charsAfter = gson.fromJson(json, Chars.class);
+ }
+
public void testTopLevelCollectionOfIntegersSerialization() {
Collection<Integer> target = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
Type targetType = new TypeToken<Collection<Integer>>() {}.getType();
@@ -413,4 +435,31 @@ public class CollectionTest extends TestCase {
assertEquals("hello", small.inSmall);
}
+ private static final class Chars implements Iterable<Character> {
+
+ private final char from;
+ private final char to;
+
+ private Chars(char from, char to) {
+ this.from = from;
+ this.to = to;
+ }
+
+ @Override public Iterator<Character> iterator() {
+ return new Iterator<Character>() {
+ private char i = from;
+ @Override public boolean hasNext() {
+ return i <= to;
+ }
+ @Override public Character next() {
+ if ( !hasNext() ) {
+ throw new NoSuchElementException();
+ }
+ return i++;
+ }
+ @Override public void remove() { throw new UnsupportedOperationException(); }
+ };
+ }
+ }
+
}
--
2.26.2 |
@lyubomyr-shaydariv, actually breaking symmetry is not only a problem for your pull request, but a general one, see #1708. |
@Marcono1234
In short, I don't think Gson should support |
Yes, that is why the pull request I created only supports |
No description provided.