Skip to content

Commit

Permalink
Merge pull request #316 from square/jwilson.0526.delegate_core_adapter
Browse files Browse the repository at this point in the history
Change the adapter for Object.class to delegate.
  • Loading branch information
swankjesse authored May 27, 2017
2 parents cda6bb9 + 9e9655b commit aee3216
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 1 deletion.
35 changes: 34 additions & 1 deletion moshi/src/main/java/com/squareup/moshi/StandardJsonAdapters.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

Expand Down Expand Up @@ -266,13 +267,45 @@ static final class EnumJsonAdapter<T extends Enum<T>> extends JsonAdapter<T> {
*/
static final class ObjectJsonAdapter extends JsonAdapter<Object> {
private final Moshi moshi;
private final JsonAdapter<List> listJsonAdapter;
private final JsonAdapter<Map> mapAdapter;
private final JsonAdapter<String> stringAdapter;
private final JsonAdapter<Double> doubleAdapter;
private final JsonAdapter<Boolean> booleanAdapter;

ObjectJsonAdapter(Moshi moshi) {
this.moshi = moshi;
this.listJsonAdapter = moshi.adapter(List.class);
this.mapAdapter = moshi.adapter(Map.class);
this.stringAdapter = moshi.adapter(String.class);
this.doubleAdapter = moshi.adapter(Double.class);
this.booleanAdapter = moshi.adapter(Boolean.class);
}

@Override public Object fromJson(JsonReader reader) throws IOException {
return reader.readJsonValue();
switch (reader.peek()) {
case BEGIN_ARRAY:
return listJsonAdapter.fromJson(reader);

case BEGIN_OBJECT:
return mapAdapter.fromJson(reader);

case STRING:
return stringAdapter.fromJson(reader);

case NUMBER:
return doubleAdapter.fromJson(reader);

case BOOLEAN:
return booleanAdapter.fromJson(reader);

case NULL:
return reader.nextNull();

default:
throw new IllegalStateException(
"Expected a value but was " + reader.peek() + " at path " + reader.getPath());
}
}

@Override public void toJson(JsonWriter writer, Object value) throws IOException {
Expand Down
110 changes: 110 additions & 0 deletions moshi/src/test/java/com/squareup/moshi/ObjectAdapterTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
*/
package com.squareup.moshi;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.AbstractCollection;
import java.util.AbstractList;
import java.util.AbstractMap;
Expand All @@ -26,12 +30,17 @@
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
import org.junit.Ignore;
import org.junit.Test;

import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;

public final class ObjectAdapterTest {
@Test public void toJsonUsesRuntimeType() throws Exception {
Expand Down Expand Up @@ -170,6 +179,107 @@ public final class ObjectAdapterTest {
assertThat(adapter.toJson(Arrays.asList(new Date(1), new Date(2)))).isEqualTo("[1,2]");
}

/**
* Confirm that the built-in adapter for Object delegates to user-supplied adapters for JSON value
* types like strings.
*/
@Test public void objectAdapterDelegatesStringNamesAndValues() throws Exception {
JsonAdapter<String> stringAdapter = new JsonAdapter<String>() {
@Nullable @Override public String fromJson(JsonReader reader) throws IOException {
return reader.nextString().toUpperCase(Locale.US);
}

@Override public void toJson(JsonWriter writer, @Nullable String value) {
throw new UnsupportedOperationException();
}
};

Moshi moshi = new Moshi.Builder()
.add(String.class, stringAdapter)
.build();
JsonAdapter<Object> objectAdapter = moshi.adapter(Object.class);
Map<?, ?> value = (Map<?, ?>) objectAdapter.fromJson("{\"a\":\"b\", \"c\":\"d\"}");
assertThat(value).containsExactly(entry("A", "B"), entry("C", "D"));
}

/**
* Confirm that the built-in adapter for Object delegates to any user-supplied adapters for
* Object. This is necessary to customize adapters for primitives like numbers.
*/
@Test public void objectAdapterDelegatesObjects() throws Exception {
JsonAdapter.Factory objectFactory = new JsonAdapter.Factory() {
@Override public @Nullable JsonAdapter<?> create(
Type type, Set<? extends Annotation> annotations, Moshi moshi) {
if (type != Object.class) return null;

final JsonAdapter<Object> delegate = moshi.nextAdapter(this, Object.class, annotations);
return new JsonAdapter<Object>() {
@Override public @Nullable Object fromJson(JsonReader reader) throws IOException {
if (reader.peek() != JsonReader.Token.NUMBER) {
return delegate.fromJson(reader);
} else {
return new BigDecimal(reader.nextString());
}
}

@Override public void toJson(JsonWriter writer, @Nullable Object value) {
throw new UnsupportedOperationException();
}
};
}
};

Moshi moshi = new Moshi.Builder()
.add(objectFactory)
.build();
JsonAdapter<Object> objectAdapter = moshi.adapter(Object.class);
List<?> value = (List<?>) objectAdapter.fromJson("[0, 1, 2.0, 3.14]");
assertThat(value).isEqualTo(Arrays.asList(new BigDecimal("0"), new BigDecimal("1"),
new BigDecimal("2.0"), new BigDecimal("3.14")));
}

/** Confirm that the built-in adapter for Object delegates to user-supplied adapters for lists. */
@Test public void objectAdapterDelegatesLists() throws Exception {
JsonAdapter<List<?>> listAdapter = new JsonAdapter<List<?>>() {
@Override public @Nullable List<?> fromJson(JsonReader reader) throws IOException {
reader.skipValue();
return singletonList("z");
}

@Override public void toJson(JsonWriter writer, @Nullable List<?> value) {
throw new UnsupportedOperationException();
}
};

Moshi moshi = new Moshi.Builder()
.add(List.class, listAdapter)
.build();
JsonAdapter<Object> objectAdapter = moshi.adapter(Object.class);
Map<?, ?> mapOfList = (Map<?, ?>) objectAdapter.fromJson("{\"a\":[\"b\"]}");
assertThat(mapOfList).isEqualTo(singletonMap("a", singletonList("z")));
}

/** Confirm that the built-in adapter for Object delegates to user-supplied adapters for maps. */
@Test public void objectAdapterDelegatesMaps() throws Exception {
JsonAdapter<Map<?, ?>> mapAdapter = new JsonAdapter<Map<?, ?>>() {
@Override public @Nullable Map<?, ?> fromJson(JsonReader reader) throws IOException {
reader.skipValue();
return singletonMap("x", "y");
}

@Override public void toJson(JsonWriter writer, @Nullable Map<?, ?> value) {
throw new UnsupportedOperationException();
}
};

Moshi moshi = new Moshi.Builder()
.add(Map.class, mapAdapter)
.build();
JsonAdapter<Object> objectAdapter = moshi.adapter(Object.class);
List<?> listOfMap = (List<?>) objectAdapter.fromJson("[{\"b\":\"c\"}]");
assertThat(listOfMap).isEqualTo(singletonList(singletonMap("x", "y")));
}

static class Delivery {
String address;
List<Object> items;
Expand Down

0 comments on commit aee3216

Please sign in to comment.