mirror of
https://github.com/fankes/moshi.git
synced 2025-10-19 07:59:21 +08:00
Add EnumJsonAdapter to adapters module. (#607)
This commit is contained in:
@@ -0,0 +1,88 @@
|
|||||||
|
package com.squareup.moshi.adapters;
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json;
|
||||||
|
import com.squareup.moshi.JsonAdapter;
|
||||||
|
import com.squareup.moshi.JsonDataException;
|
||||||
|
import com.squareup.moshi.JsonReader;
|
||||||
|
import com.squareup.moshi.JsonWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A JsonAdapter for enums that allows having a fallback enum value when a deserialized string does
|
||||||
|
* not match any enum value.
|
||||||
|
*/
|
||||||
|
public final class EnumJsonAdapter<T extends Enum<T>> extends JsonAdapter<T> {
|
||||||
|
final Class<T> enumType;
|
||||||
|
final String[] nameStrings;
|
||||||
|
final T[] constants;
|
||||||
|
final JsonReader.Options options;
|
||||||
|
final @Nullable T fallbackValue;
|
||||||
|
|
||||||
|
public static <T extends Enum<T>> EnumJsonAdapter<T> create(Class<T> enumType) {
|
||||||
|
return new EnumJsonAdapter<>(enumType, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new adapter for this enum with a fallback value to use when the JSON string does not
|
||||||
|
* match any of the enum's constants. Note that this value will not be used when the JSON value is
|
||||||
|
* null, absent, or not a string. Also, the string values are case-sensitive, and this fallback
|
||||||
|
* value will be used even on case mismatches.
|
||||||
|
*/
|
||||||
|
public EnumJsonAdapter<T> withUnknownFallback(T fallbackValue) {
|
||||||
|
if (fallbackValue == null) {
|
||||||
|
throw new NullPointerException("fallbackValue == null");
|
||||||
|
}
|
||||||
|
return new EnumJsonAdapter<>(enumType, fallbackValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
EnumJsonAdapter(Class<T> enumType, @Nullable T fallbackValue) {
|
||||||
|
this.enumType = enumType;
|
||||||
|
this.fallbackValue = fallbackValue;
|
||||||
|
try {
|
||||||
|
constants = enumType.getEnumConstants();
|
||||||
|
nameStrings = new String[constants.length];
|
||||||
|
for (int i = 0; i < constants.length; i++) {
|
||||||
|
String constantName = constants[i].name();
|
||||||
|
Json annotation = enumType.getField(constantName).getAnnotation(Json.class);
|
||||||
|
String name = annotation != null ? annotation.name() : constantName;
|
||||||
|
nameStrings[i] = name;
|
||||||
|
}
|
||||||
|
options = JsonReader.Options.of(nameStrings);
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
throw new AssertionError("Missing field in " + enumType.getName(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public @Nonnull T fromJson(JsonReader reader) throws IOException {
|
||||||
|
int index = reader.selectString(options);
|
||||||
|
if (index != -1) return constants[index];
|
||||||
|
|
||||||
|
String path = reader.getPath();
|
||||||
|
if (fallbackValue == null) {
|
||||||
|
String name = reader.nextString();
|
||||||
|
throw new JsonDataException("Expected one of "
|
||||||
|
+ Arrays.asList(nameStrings) + " but was " + name + " at path " + path);
|
||||||
|
}
|
||||||
|
if (reader.peek() != JsonReader.Token.STRING) {
|
||||||
|
throw new JsonDataException(
|
||||||
|
"Expected a string but was " + reader.peek() + " at path " + path);
|
||||||
|
}
|
||||||
|
reader.skipValue();
|
||||||
|
return fallbackValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void toJson(JsonWriter writer, T value) throws IOException {
|
||||||
|
if (value == null) {
|
||||||
|
throw new NullPointerException(
|
||||||
|
"value was null! Wrap in .nullSafe() to write nullable values.");
|
||||||
|
}
|
||||||
|
writer.value(nameStrings[value.ordinal()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public String toString() {
|
||||||
|
return "EnumJsonAdapter(" + enumType.getName() + ")";
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,52 @@
|
|||||||
|
package com.squareup.moshi.adapters;
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json;
|
||||||
|
import com.squareup.moshi.JsonDataException;
|
||||||
|
import com.squareup.moshi.JsonReader;
|
||||||
|
import okio.Buffer;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
@SuppressWarnings("CheckReturnValue")
|
||||||
|
public final class EnumJsonAdapterTest {
|
||||||
|
@Test public void toAndFromJson() throws Exception {
|
||||||
|
EnumJsonAdapter<Roshambo> adapter = EnumJsonAdapter.create(Roshambo.class);
|
||||||
|
assertThat(adapter.fromJson("\"ROCK\"")).isEqualTo(Roshambo.ROCK);
|
||||||
|
assertThat(adapter.toJson(Roshambo.PAPER)).isEqualTo("\"PAPER\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test public void withJsonName() throws Exception {
|
||||||
|
EnumJsonAdapter<Roshambo> adapter = EnumJsonAdapter.create(Roshambo.class);
|
||||||
|
assertThat(adapter.fromJson("\"scr\"")).isEqualTo(Roshambo.SCISSORS);
|
||||||
|
assertThat(adapter.toJson(Roshambo.SCISSORS)).isEqualTo("\"scr\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test public void withoutFallbackValue() throws Exception {
|
||||||
|
EnumJsonAdapter<Roshambo> adapter = EnumJsonAdapter.create(Roshambo.class);
|
||||||
|
JsonReader reader = JsonReader.of(new Buffer().writeUtf8("\"SPOCK\""));
|
||||||
|
try {
|
||||||
|
adapter.fromJson(reader);
|
||||||
|
fail();
|
||||||
|
} catch (JsonDataException expected) {
|
||||||
|
assertThat(expected).hasMessage(
|
||||||
|
"Expected one of [ROCK, PAPER, scr] but was SPOCK at path $");
|
||||||
|
}
|
||||||
|
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test public void withFallbackValue() throws Exception {
|
||||||
|
EnumJsonAdapter<Roshambo> adapter = EnumJsonAdapter.create(Roshambo.class)
|
||||||
|
.withUnknownFallback(Roshambo.ROCK);
|
||||||
|
JsonReader reader = JsonReader.of(new Buffer().writeUtf8("\"SPOCK\""));
|
||||||
|
assertThat(adapter.fromJson(reader)).isEqualTo(Roshambo.ROCK);
|
||||||
|
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Roshambo {
|
||||||
|
ROCK,
|
||||||
|
PAPER,
|
||||||
|
@Json(name = "scr") SCISSORS
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user