mirror of
https://github.com/fankes/moshi.git
synced 2025-10-18 23:49:21 +08:00
Initial JsonAdapter structure.
This is similar to the way Gson does its type adapters: factories that can delegate and compose. It's different because annotations are fundamental to the design. It's also different because there are no APIs to convert to and from JSON on the central object: instead callers must get the JSON adapter they're interested in and do that there. This is potentially more efficient because applications can hold the adapter they need.
This commit is contained in:
@@ -22,5 +22,10 @@
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
101
moshi/src/main/java/com/squareup/moshi/JsonAdapter.java
Normal file
101
moshi/src/main/java/com/squareup/moshi/JsonAdapter.java
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.io.StringWriter;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* Converts Java values to JSON, and JSON values to Java.
|
||||
*/
|
||||
public abstract class JsonAdapter<T> {
|
||||
public abstract T fromJson(JsonReader reader) throws IOException;
|
||||
|
||||
public final T fromJson(String string) throws IOException {
|
||||
return fromJson(new JsonReader(string));
|
||||
}
|
||||
|
||||
public abstract void toJson(JsonWriter writer, T value) throws IOException;
|
||||
|
||||
public final String toJson(T value) throws IOException {
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
toJson(new JsonWriter(stringWriter), value);
|
||||
return stringWriter.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a JSON adapter equal to this JSON adapter, but with support for reading and writing
|
||||
* nulls.
|
||||
*/
|
||||
public final JsonAdapter<T> nullSafe() {
|
||||
final JsonAdapter<T> delegate = this;
|
||||
return new JsonAdapter<T>() {
|
||||
@Override public T fromJson(JsonReader reader) throws IOException {
|
||||
if (reader.peek() == JsonToken.NULL) {
|
||||
return reader.nextNull();
|
||||
} else {
|
||||
return delegate.fromJson(reader);
|
||||
}
|
||||
}
|
||||
@Override public void toJson(JsonWriter writer, T value) throws IOException {
|
||||
if (value == null) {
|
||||
writer.nullValue();
|
||||
} else {
|
||||
delegate.toJson(writer, value);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** Returns a JSON adapter equal to this JSON adapter, but is lenient when reading and writing. */
|
||||
public final JsonAdapter<T> lenient() {
|
||||
final JsonAdapter<T> delegate = this;
|
||||
return new JsonAdapter<T>() {
|
||||
@Override public T fromJson(JsonReader reader) throws IOException {
|
||||
boolean lenient = reader.isLenient();
|
||||
reader.setLenient(true);
|
||||
try {
|
||||
return delegate.fromJson(reader);
|
||||
} finally {
|
||||
reader.setLenient(lenient);
|
||||
}
|
||||
}
|
||||
@Override public void toJson(JsonWriter writer, T value) throws IOException {
|
||||
boolean lenient = writer.isLenient();
|
||||
writer.setLenient(true);
|
||||
try {
|
||||
delegate.toJson(writer, value);
|
||||
} finally {
|
||||
writer.setLenient(lenient);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public interface Factory {
|
||||
/**
|
||||
* Attempts to create an adapter for {@code type} annotated with {@code annotations}. This
|
||||
* returns the adapter if one was created, or null if this factory isn't capable of creating
|
||||
* such an adapter.
|
||||
*
|
||||
* <p>Implementations may use to {@link Moshi#adapter} to compose adapters of other types, or
|
||||
* {@link Moshi#nextAdapter} to delegate to the underlying adapter of the same type.
|
||||
*/
|
||||
JsonAdapter<?> create(Type type, AnnotatedElement annotations, Moshi moshi);
|
||||
}
|
||||
}
|
@@ -214,7 +214,6 @@ public class JsonReader implements Closeable {
|
||||
private static final int NUMBER_CHAR_EXP_SIGN = 6;
|
||||
private static final int NUMBER_CHAR_EXP_DIGIT = 7;
|
||||
|
||||
|
||||
/** True to accept non-spec compliant JSON */
|
||||
private boolean lenient = false;
|
||||
|
||||
@@ -843,12 +842,12 @@ public class JsonReader implements Closeable {
|
||||
|
||||
/**
|
||||
* Consumes the next token from the JSON stream and asserts that it is a
|
||||
* literal null.
|
||||
* literal null. Returns null.
|
||||
*
|
||||
* @throws IllegalStateException if the next token is not null or if this
|
||||
* reader is closed.
|
||||
*/
|
||||
public void nextNull() throws IOException {
|
||||
public <T> T nextNull() throws IOException {
|
||||
int p = peeked;
|
||||
if (p == PEEKED_NONE) {
|
||||
p = doPeek();
|
||||
@@ -856,6 +855,7 @@ public class JsonReader implements Closeable {
|
||||
if (p == PEEKED_NULL) {
|
||||
peeked = PEEKED_NONE;
|
||||
pathIndices[stackSize - 1]++;
|
||||
return null;
|
||||
} else {
|
||||
throw new IllegalStateException("Expected null but was " + peek()
|
||||
+ " at path " + getPath());
|
||||
|
89
moshi/src/main/java/com/squareup/moshi/Moshi.java
Normal file
89
moshi/src/main/java/com/squareup/moshi/Moshi.java
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Coordinates binding between JSON values and Java objects.
|
||||
*/
|
||||
public final class Moshi {
|
||||
private final List<JsonAdapter.Factory> factories;
|
||||
|
||||
private Moshi(Builder builder) {
|
||||
List<JsonAdapter.Factory> factories = new ArrayList<JsonAdapter.Factory>();
|
||||
factories.addAll(builder.factories);
|
||||
factories.add(new StandardJsonAdapterFactory());
|
||||
this.factories = Collections.unmodifiableList(factories);
|
||||
}
|
||||
|
||||
public <T> JsonAdapter<T> adapter(Type type) {
|
||||
return adapter(type, Util.NO_ANNOTATIONS);
|
||||
}
|
||||
|
||||
/** Returns a JSON adapter for {@code type}, creating it if necessary. */
|
||||
public <T> JsonAdapter<T> adapter(Class<T> type) {
|
||||
// TODO: cache created JSON adapters.
|
||||
return adapter(type, Util.NO_ANNOTATIONS);
|
||||
}
|
||||
|
||||
public <T> JsonAdapter<T> adapter(Type type, AnnotatedElement annotations) {
|
||||
// TODO: support re-entrant calls.
|
||||
return createAdapter(0, type, annotations);
|
||||
}
|
||||
|
||||
public <T> JsonAdapter<T> nextAdapter(JsonAdapter.Factory skipPast, Type type,
|
||||
AnnotatedElement annotations) {
|
||||
return createAdapter(factories.indexOf(skipPast) + 1, type, annotations);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked") // Factories are required to return only matching JsonAdapters.
|
||||
private <T> JsonAdapter<T> createAdapter(
|
||||
int firstIndex, Type type, AnnotatedElement annotations) {
|
||||
for (int i = firstIndex, size = factories.size(); i < size; i++) {
|
||||
JsonAdapter<?> result = factories.get(i).create(type, annotations, this);
|
||||
if (result != null) return (JsonAdapter<T>) result;
|
||||
}
|
||||
throw new IllegalArgumentException("no JsonAdapter for " + type);
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
private final List<JsonAdapter.Factory> factories = new ArrayList<JsonAdapter.Factory>();
|
||||
|
||||
public <T> Builder add(final Type type, final JsonAdapter<T> jsonAdapter) {
|
||||
return add(new JsonAdapter.Factory() {
|
||||
@Override public JsonAdapter<?> create(
|
||||
Type targetType, AnnotatedElement annotations, Moshi moshi) {
|
||||
return Util.typesMatch(type, targetType) ? jsonAdapter : null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Builder add(JsonAdapter.Factory jsonAdapter) {
|
||||
// TODO: define precedence order. Last added wins? First added wins?
|
||||
factories.add(jsonAdapter);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Moshi build() {
|
||||
return new Moshi(this);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.Type;
|
||||
|
||||
final class StandardJsonAdapterFactory implements JsonAdapter.Factory {
|
||||
static final JsonAdapter<Boolean> BOOLEAN_JSON_ADAPTER = new JsonAdapter<Boolean>() {
|
||||
@Override public Boolean fromJson(JsonReader reader) throws IOException {
|
||||
return reader.nextBoolean();
|
||||
}
|
||||
@Override public void toJson(JsonWriter writer, Boolean value) throws IOException {
|
||||
writer.value(value);
|
||||
}
|
||||
};
|
||||
|
||||
static final JsonAdapter<Double> DOUBLE_JSON_ADAPTER = new JsonAdapter<Double>() {
|
||||
@Override public Double fromJson(JsonReader reader) throws IOException {
|
||||
return reader.nextDouble();
|
||||
}
|
||||
@Override public void toJson(JsonWriter writer, Double value) throws IOException {
|
||||
writer.value(value);
|
||||
}
|
||||
};
|
||||
|
||||
static final JsonAdapter<Integer> INTEGER_JSON_ADAPTER = new JsonAdapter<Integer>() {
|
||||
@Override public Integer fromJson(JsonReader reader) throws IOException {
|
||||
return reader.nextInt();
|
||||
}
|
||||
@Override public void toJson(JsonWriter writer, Integer value) throws IOException {
|
||||
writer.value(value.intValue());
|
||||
}
|
||||
};
|
||||
|
||||
static final JsonAdapter<String> STRING_JSON_ADAPTER = new JsonAdapter<String>() {
|
||||
@Override public String fromJson(JsonReader reader) throws IOException {
|
||||
return reader.nextString();
|
||||
}
|
||||
@Override public void toJson(JsonWriter writer, String value) throws IOException {
|
||||
writer.value(value);
|
||||
}
|
||||
};
|
||||
|
||||
@Override public JsonAdapter<?> create(
|
||||
Type type, AnnotatedElement annotations, Moshi moshi) {
|
||||
// TODO: support all 8 primitive types.
|
||||
if (type == boolean.class) return BOOLEAN_JSON_ADAPTER;
|
||||
if (type == double.class) return DOUBLE_JSON_ADAPTER;
|
||||
if (type == int.class) return INTEGER_JSON_ADAPTER;
|
||||
if (type == Boolean.class) return BOOLEAN_JSON_ADAPTER.nullSafe();
|
||||
if (type == Double.class) return DOUBLE_JSON_ADAPTER.nullSafe();
|
||||
if (type == Integer.class) return INTEGER_JSON_ADAPTER.nullSafe();
|
||||
if (type == String.class) return STRING_JSON_ADAPTER.nullSafe();
|
||||
return null;
|
||||
}
|
||||
}
|
44
moshi/src/main/java/com/squareup/moshi/Util.java
Normal file
44
moshi/src/main/java/com/squareup/moshi/Util.java
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
final class Util {
|
||||
public static final Annotation[] EMPTY_ANNOTATIONS_ARRAY = new Annotation[0];
|
||||
|
||||
public static final AnnotatedElement NO_ANNOTATIONS = new AnnotatedElement() {
|
||||
@Override public boolean isAnnotationPresent(Class<? extends Annotation> aClass) {
|
||||
return false;
|
||||
}
|
||||
@Override public <T extends Annotation> T getAnnotation(Class<T> tClass) {
|
||||
return null;
|
||||
}
|
||||
@Override public Annotation[] getAnnotations() {
|
||||
return EMPTY_ANNOTATIONS_ARRAY;
|
||||
}
|
||||
@Override public Annotation[] getDeclaredAnnotations() {
|
||||
return EMPTY_ANNOTATIONS_ARRAY;
|
||||
}
|
||||
};
|
||||
|
||||
public static boolean typesMatch(Type pattern, Type candidate) {
|
||||
// TODO: permit raw types (like Set.class) to match non-raw candidates (like Set<Long>).
|
||||
return pattern.equals(candidate);
|
||||
}
|
||||
}
|
225
moshi/src/test/java/com/squareup/moshi/MoshiTest.java
Normal file
225
moshi/src/test/java/com/squareup/moshi/MoshiTest.java
Normal file
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* 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.annotation.Retention;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Locale;
|
||||
import org.junit.Test;
|
||||
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public final class MoshiTest {
|
||||
/** No nulls for int.class. */
|
||||
@Test public void intAdapter() throws Exception {
|
||||
Moshi moshi = new Moshi.Builder().build();
|
||||
JsonAdapter<Integer> adapter = moshi.adapter(int.class).lenient();
|
||||
assertThat(adapter.fromJson("1")).isEqualTo(1);
|
||||
assertThat(adapter.toJson(2)).isEqualTo("2");
|
||||
|
||||
try {
|
||||
adapter.fromJson("null");
|
||||
fail();
|
||||
} catch (IllegalStateException expected) {
|
||||
assertThat(expected.getMessage()).isEqualTo("Expected an int but was NULL at path $");
|
||||
}
|
||||
|
||||
try {
|
||||
moshi.adapter(int.class).toJson(null);
|
||||
fail();
|
||||
} catch (NullPointerException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
/** Moshi supports nulls for Integer.class. */
|
||||
@Test public void integerAdapter() throws Exception {
|
||||
Moshi moshi = new Moshi.Builder().build();
|
||||
JsonAdapter<Integer> adapter = moshi.adapter(Integer.class).lenient();
|
||||
assertThat(adapter.fromJson("1")).isEqualTo(1);
|
||||
assertThat(adapter.toJson(2)).isEqualTo("2");
|
||||
assertThat(adapter.fromJson("null")).isEqualTo(null);
|
||||
assertThat(adapter.toJson(null)).isEqualTo("null");
|
||||
}
|
||||
|
||||
@Test public void stringAdapter() throws Exception {
|
||||
Moshi moshi = new Moshi.Builder().build();
|
||||
JsonAdapter<String> adapter = moshi.adapter(String.class).lenient();
|
||||
assertThat(adapter.fromJson("\"a\"")).isEqualTo("a");
|
||||
assertThat(adapter.toJson("b")).isEqualTo("\"b\"");
|
||||
assertThat(adapter.fromJson("null")).isEqualTo(null);
|
||||
assertThat(adapter.toJson(null)).isEqualTo("null");
|
||||
}
|
||||
|
||||
@Test public void customJsonAdapter() throws Exception {
|
||||
Moshi moshi = new Moshi.Builder()
|
||||
.add(Pizza.class, new PizzaAdapter())
|
||||
.build();
|
||||
|
||||
JsonAdapter<Pizza> jsonAdapter = moshi.adapter(Pizza.class);
|
||||
assertThat(jsonAdapter.toJson(new Pizza(15, true)))
|
||||
.isEqualTo("{\"size\":15,\"extra cheese\":true}");
|
||||
assertThat(jsonAdapter.fromJson("{\"extra cheese\":true,\"size\":18}"))
|
||||
.isEqualTo(new Pizza(18, true));
|
||||
}
|
||||
|
||||
@Test public void composingJsonAdapterFactory() throws Exception {
|
||||
Moshi moshi = new Moshi.Builder()
|
||||
.add(new MealDealAdapterFactory())
|
||||
.add(Pizza.class, new PizzaAdapter())
|
||||
.build();
|
||||
|
||||
JsonAdapter<MealDeal> jsonAdapter = moshi.adapter(MealDeal.class);
|
||||
assertThat(jsonAdapter.toJson(new MealDeal(new Pizza(15, true), "Pepsi")))
|
||||
.isEqualTo("[{\"size\":15,\"extra cheese\":true},\"Pepsi\"]");
|
||||
assertThat(jsonAdapter.fromJson("[{\"extra cheese\":true,\"size\":18},\"Coke\"]"))
|
||||
.isEqualTo(new MealDeal(new Pizza(18, true), "Coke"));
|
||||
}
|
||||
|
||||
@Uppercase
|
||||
static String uppercaseString;
|
||||
|
||||
@Test public void delegatingJsonAdapterFactory() throws Exception {
|
||||
Moshi moshi = new Moshi.Builder()
|
||||
.add(new UppercaseAdapterFactory())
|
||||
.build();
|
||||
|
||||
AnnotatedElement annotations = MoshiTest.class.getDeclaredField("uppercaseString");
|
||||
JsonAdapter<String> adapter = moshi.<String>adapter(String.class, annotations).lenient();
|
||||
assertThat(adapter.toJson("a")).isEqualTo("\"A\"");
|
||||
assertThat(adapter.fromJson("\"b\"")).isEqualTo("B");
|
||||
}
|
||||
|
||||
static class Pizza {
|
||||
final int diameter;
|
||||
final boolean extraCheese;
|
||||
|
||||
Pizza(int diameter, boolean extraCheese) {
|
||||
this.diameter = diameter;
|
||||
this.extraCheese = extraCheese;
|
||||
}
|
||||
|
||||
@Override public boolean equals(Object o) {
|
||||
return o instanceof Pizza
|
||||
&& ((Pizza) o).diameter == diameter
|
||||
&& ((Pizza) o).extraCheese == extraCheese;
|
||||
}
|
||||
|
||||
@Override public int hashCode() {
|
||||
return diameter * (extraCheese ? 31 : 1);
|
||||
}
|
||||
}
|
||||
|
||||
static class MealDeal {
|
||||
final Pizza pizza;
|
||||
final String drink;
|
||||
|
||||
MealDeal(Pizza pizza, String drink) {
|
||||
this.pizza = pizza;
|
||||
this.drink = drink;
|
||||
}
|
||||
|
||||
@Override public boolean equals(Object o) {
|
||||
return o instanceof MealDeal
|
||||
&& ((MealDeal) o).pizza.equals(pizza)
|
||||
&& ((MealDeal) o).drink.equals(drink);
|
||||
}
|
||||
|
||||
@Override public int hashCode() {
|
||||
return pizza.hashCode() + (31 * drink.hashCode());
|
||||
}
|
||||
}
|
||||
|
||||
static class PizzaAdapter extends JsonAdapter<Pizza> {
|
||||
@Override public Pizza fromJson(JsonReader reader) throws IOException {
|
||||
int diameter = 13;
|
||||
boolean extraCheese = false;
|
||||
reader.beginObject();
|
||||
while (reader.hasNext()) {
|
||||
String name = reader.nextName();
|
||||
if (name.equals("size")) {
|
||||
diameter = reader.nextInt();
|
||||
} else if (name.equals("extra cheese")) {
|
||||
extraCheese = reader.nextBoolean();
|
||||
} else {
|
||||
reader.skipValue();
|
||||
}
|
||||
}
|
||||
reader.endObject();
|
||||
return new Pizza(diameter, extraCheese);
|
||||
}
|
||||
|
||||
@Override public void toJson(JsonWriter writer, Pizza value) throws IOException {
|
||||
writer.beginObject();
|
||||
writer.name("size").value(value.diameter);
|
||||
writer.name("extra cheese").value(value.extraCheese);
|
||||
writer.endObject();
|
||||
}
|
||||
}
|
||||
|
||||
static class MealDealAdapterFactory implements JsonAdapter.Factory {
|
||||
@Override public JsonAdapter<?> create(
|
||||
Type type, AnnotatedElement annotations, Moshi moshi) {
|
||||
if (!type.equals(MealDeal.class)) return null;
|
||||
|
||||
final JsonAdapter<Pizza> pizzaAdapter = moshi.adapter(Pizza.class);
|
||||
final JsonAdapter<String> drinkAdapter = moshi.adapter(String.class);
|
||||
return new JsonAdapter<MealDeal>() {
|
||||
@Override public MealDeal fromJson(JsonReader reader) throws IOException {
|
||||
reader.beginArray();
|
||||
Pizza pizza = pizzaAdapter.fromJson(reader);
|
||||
String drink = drinkAdapter.fromJson(reader);
|
||||
reader.endArray();
|
||||
return new MealDeal(pizza, drink);
|
||||
}
|
||||
|
||||
@Override public void toJson(JsonWriter writer, MealDeal value) throws IOException {
|
||||
writer.beginArray();
|
||||
pizzaAdapter.toJson(writer, value.pizza);
|
||||
drinkAdapter.toJson(writer, value.drink);
|
||||
writer.endArray();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Retention(RUNTIME)
|
||||
public @interface Uppercase {
|
||||
}
|
||||
|
||||
static class UppercaseAdapterFactory implements JsonAdapter.Factory {
|
||||
@Override public JsonAdapter<?> create(
|
||||
Type type, AnnotatedElement annotations, Moshi moshi) {
|
||||
if (!type.equals(String.class)) return null;
|
||||
if (!annotations.isAnnotationPresent(Uppercase.class)) return null;
|
||||
|
||||
final JsonAdapter<String> stringAdapter = moshi.nextAdapter(this, String.class, annotations);
|
||||
return new JsonAdapter<String>() {
|
||||
@Override public String fromJson(JsonReader reader) throws IOException {
|
||||
String s = stringAdapter.fromJson(reader);
|
||||
return s.toUpperCase(Locale.US);
|
||||
}
|
||||
|
||||
@Override public void toJson(JsonWriter writer, String value) throws IOException {
|
||||
stringAdapter.toJson(writer, value.toUpperCase());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
6
pom.xml
6
pom.xml
@@ -30,6 +30,7 @@
|
||||
|
||||
<!-- Test Dependencies -->
|
||||
<junit.version>4.11</junit.version>
|
||||
<assertj.version>1.6.1</assertj.version>
|
||||
</properties>
|
||||
|
||||
<scm>
|
||||
@@ -63,6 +64,11 @@
|
||||
<artifactId>okio</artifactId>
|
||||
<version>${okio.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<version>${assertj.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
Reference in New Issue
Block a user