From a067ecc55dac0662d572535c0acbb9d2a3331981 Mon Sep 17 00:00:00 2001 From: Jesse Wilson Date: Tue, 12 Aug 2014 09:40:19 -0400 Subject: [PATCH] Implement an array adapter. --- .../com/squareup/moshi/ArrayJsonAdapter.java | 69 +++++++++++++++++++ .../squareup/moshi/CollectionJsonAdapter.java | 14 ++-- .../main/java/com/squareup/moshi/Moshi.java | 1 + .../main/java/com/squareup/moshi/Types.java | 16 +++-- .../java/com/squareup/moshi/MoshiTest.java | 20 +++++- 5 files changed, 103 insertions(+), 17 deletions(-) create mode 100644 moshi/src/main/java/com/squareup/moshi/ArrayJsonAdapter.java diff --git a/moshi/src/main/java/com/squareup/moshi/ArrayJsonAdapter.java b/moshi/src/main/java/com/squareup/moshi/ArrayJsonAdapter.java new file mode 100644 index 0000000..7c2b933 --- /dev/null +++ b/moshi/src/main/java/com/squareup/moshi/ArrayJsonAdapter.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2014 Square, Inc. + * + * Licensed 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 com.squareup.moshi; + +import java.io.IOException; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Array; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; + +/** + * Converts arrays to JSON arrays containing their converted contents. This + * supports both primitive and object arrays. + */ +class ArrayJsonAdapter extends JsonAdapter { + public static final Factory FACTORY = new Factory() { + @Override public JsonAdapter create(Type type, AnnotatedElement annotations, Moshi moshi) { + Type elementType = Types.arrayComponentType(type); + if (elementType == null) return null; + Class elementClass = Types.getRawType(elementType); + JsonAdapter elementAdapter = moshi.adapter(elementType); + return new ArrayJsonAdapter(elementClass, elementAdapter).nullSafe(); + } + }; + + private final Class elementClass; + private final JsonAdapter elementAdapter; + + ArrayJsonAdapter(Class elementClass, JsonAdapter elementAdapter) { + this.elementClass = elementClass; + this.elementAdapter = elementAdapter; + } + + @Override public Object fromJson(JsonReader reader) throws IOException { + List list = new ArrayList(); + reader.beginArray(); + while (reader.hasNext()) { + list.add(elementAdapter.fromJson(reader)); + } + reader.endArray(); + Object array = Array.newInstance(elementClass, list.size()); + for (int i = 0; i < list.size(); i++) { + Array.set(array, i, list.get(i)); + } + return array; + } + + @Override public void toJson(JsonWriter writer, Object value) throws IOException { + writer.beginArray(); + for (int i = 0, size = Array.getLength(value); i < size; i++) { + elementAdapter.toJson(writer, Array.get(value, i)); + } + writer.endArray(); + } +} diff --git a/moshi/src/main/java/com/squareup/moshi/CollectionJsonAdapter.java b/moshi/src/main/java/com/squareup/moshi/CollectionJsonAdapter.java index 5df57a0..25acb3a 100644 --- a/moshi/src/main/java/com/squareup/moshi/CollectionJsonAdapter.java +++ b/moshi/src/main/java/com/squareup/moshi/CollectionJsonAdapter.java @@ -30,9 +30,9 @@ abstract class CollectionJsonAdapter, T> extends JsonAda @Override public JsonAdapter create(Type type, AnnotatedElement annotations, Moshi moshi) { Class rawType = Types.getRawType(type); if (rawType == List.class || rawType == Collection.class) { - return newArrayListAdapter(type, annotations, moshi).nullSafe(); + return newArrayListAdapter(type, moshi).nullSafe(); } else if (rawType == Set.class) { - return newLinkedHashSetAdapter(type, annotations, moshi).nullSafe(); + return newLinkedHashSetAdapter(type, moshi).nullSafe(); } return null; } @@ -44,10 +44,9 @@ abstract class CollectionJsonAdapter, T> extends JsonAda this.elementAdapter = elementAdapter; } - private static JsonAdapter> newArrayListAdapter( - Type type, AnnotatedElement annotations, Moshi moshi) { + private static JsonAdapter> newArrayListAdapter(Type type, Moshi moshi) { Type elementType = Types.collectionElementType(type, Collection.class); - JsonAdapter elementAdapter = moshi.adapter(elementType, annotations); + JsonAdapter elementAdapter = moshi.adapter(elementType); return new CollectionJsonAdapter, T>(elementAdapter) { @Override Collection newCollection() { return new ArrayList(); @@ -55,10 +54,9 @@ abstract class CollectionJsonAdapter, T> extends JsonAda }; } - private static JsonAdapter> newLinkedHashSetAdapter( - Type type, AnnotatedElement annotations, Moshi moshi) { + private static JsonAdapter> newLinkedHashSetAdapter(Type type, Moshi moshi) { Type elementType = Types.collectionElementType(type, Collection.class); - JsonAdapter elementAdapter = moshi.adapter(elementType, annotations); + JsonAdapter elementAdapter = moshi.adapter(elementType); return new CollectionJsonAdapter, T>(elementAdapter) { @Override Set newCollection() { return new LinkedHashSet(); diff --git a/moshi/src/main/java/com/squareup/moshi/Moshi.java b/moshi/src/main/java/com/squareup/moshi/Moshi.java index 1d31c68..aea43e5 100644 --- a/moshi/src/main/java/com/squareup/moshi/Moshi.java +++ b/moshi/src/main/java/com/squareup/moshi/Moshi.java @@ -32,6 +32,7 @@ public final class Moshi { factories.addAll(builder.factories); factories.add(StandardJsonAdapters.FACTORY); factories.add(CollectionJsonAdapter.FACTORY); + factories.add(ArrayJsonAdapter.FACTORY); this.factories = Collections.unmodifiableList(factories); } diff --git a/moshi/src/main/java/com/squareup/moshi/Types.java b/moshi/src/main/java/com/squareup/moshi/Types.java index 8c4f584..0591724 100644 --- a/moshi/src/main/java/com/squareup/moshi/Types.java +++ b/moshi/src/main/java/com/squareup/moshi/Types.java @@ -245,13 +245,17 @@ final class Types { } /** - * Returns the component type of this array type. - * @throws ClassCastException if this type is not an array. + * Returns the element type of {@code type} if it is an array type, or null if it is not an + * array type. */ - public static Type arrayComponentType(Type array) { - return array instanceof GenericArrayType - ? ((GenericArrayType) array).getGenericComponentType() - : ((Class) array).getComponentType(); + public static Type arrayComponentType(Type type) { + if (type instanceof GenericArrayType) { + return ((GenericArrayType) type).getGenericComponentType(); + } else if (type instanceof Class) { + return ((Class) type).getComponentType(); + } else { + return null; + } } /** diff --git a/moshi/src/test/java/com/squareup/moshi/MoshiTest.java b/moshi/src/test/java/com/squareup/moshi/MoshiTest.java index 1dfeb64..cd59071 100644 --- a/moshi/src/test/java/com/squareup/moshi/MoshiTest.java +++ b/moshi/src/test/java/com/squareup/moshi/MoshiTest.java @@ -147,7 +147,7 @@ public final class MoshiTest { @Uppercase static List uppercaseStrings; - @Test public void collectionsKeepAnnotations() throws Exception { + @Test public void collectionsDoNotKeepAnnotations() throws Exception { Moshi moshi = new Moshi.Builder() .add(new UppercaseAdapterFactory()) .build(); @@ -155,8 +155,22 @@ public final class MoshiTest { Field uppercaseStringsField = MoshiTest.class.getDeclaredField("uppercaseStrings"); JsonAdapter> adapter = moshi.adapter(uppercaseStringsField.getGenericType(), uppercaseStringsField); - assertThat(adapter.toJson(Arrays.asList("a"))).isEqualTo("[\"A\"]"); - assertThat(adapter.fromJson("[\"b\"]")).isEqualTo(Arrays.asList("B")); + assertThat(adapter.toJson(Arrays.asList("a"))).isEqualTo("[\"a\"]"); + assertThat(adapter.fromJson("[\"b\"]")).isEqualTo(Arrays.asList("b")); + } + + @Test public void objectArray() throws Exception { + Moshi moshi = new Moshi.Builder().build(); + JsonAdapter adapter = moshi.adapter(String[].class); + assertThat(adapter.toJson(new String[] {"a", "b"})).isEqualTo("[\"a\",\"b\"]"); + assertThat(adapter.fromJson("[\"a\",\"b\"]")).containsExactly("a", "b"); + } + + @Test public void primitiveArray() throws Exception { + Moshi moshi = new Moshi.Builder().build(); + JsonAdapter adapter = moshi.adapter(int[].class); + assertThat(adapter.toJson(new int[] {1, 2})).isEqualTo("[1,2]"); + assertThat(adapter.fromJson("[2,3]")).containsExactly(2, 3); } static class Pizza {