Simplify the internals of PolymorphicJsonAdapterFactory

We don't need a custom JsonAdapter subclass - we can just call through
unconditionally and fail if the type is unexpected.
This commit is contained in:
Jesse Wilson
2020-07-30 22:34:09 -04:00
parent cd31e5ce52
commit 2b5b3b5f8e
2 changed files with 30 additions and 54 deletions

View File

@@ -100,30 +100,22 @@ import javax.annotation.Nullable;
*
* <p>If an unknown subtype is encountered when decoding:
* <ul>
* <li> if {@link #withDefaultValue(Object)} is used, then {@code defaultValue} will be returned
* <li> if {@link #withFallbackJsonAdapter(JsonAdapter)} is used, then the
* {@code fallbackJsonAdapter.fromJson(reader)} result will be returned
* <li> otherwise a {@link JsonDataException} will be thrown
* <li>If {@link #withDefaultValue(Object)} is used, then {@code defaultValue} will be returned.
* <li>If {@link #withFallbackJsonAdapter(JsonAdapter)} is used, then the
* {@code fallbackJsonAdapter.fromJson(reader)} result will be returned.
* <li>Otherwise a {@link JsonDataException} will be thrown.
* </ul>
*
* <p>If an unknown type is encountered when encoding:
* <ul>
* <li> if {@link #withFallbackJsonAdapter(JsonAdapter)} is used, then the
* {@code fallbackJsonAdapter.toJson(writer, value)} result will be returned
* <li> otherwise a {@link IllegalArgumentException} will be thrown
* <li>If {@link #withFallbackJsonAdapter(JsonAdapter)} is used, then the
* {@code fallbackJsonAdapter.toJson(writer, value)} result will be returned.
* <li>Otherwise a {@link IllegalArgumentException} will be thrown.
* </ul>
*
* <p>If the same subtype has multiple labels the first one is used when encoding.
*/
public final class PolymorphicJsonAdapterFactory<T> implements JsonAdapter.Factory {
/**
* Thin wrapper around {@link JsonAdapter} to allow {@link PolymorphicJsonAdapter} to
* distinguish between {@code JsonAdapter} added due to a {@code defaultValue} or added
* by users of {@link PolymorphicJsonAdapterFactory}.
*/
private abstract static class DefaultJsonAdapter<T> extends JsonAdapter<T> {
}
final Class<T> baseType;
final String labelKey;
final List<String> labels;
@@ -205,19 +197,15 @@ public final class PolymorphicJsonAdapterFactory<T> implements JsonAdapter.Facto
}
private JsonAdapter<Object> buildFallbackJsonAdapter(final T defaultValue) {
return new DefaultJsonAdapter<Object>() {
@Nullable
@Override
public Object fromJson(JsonReader reader) throws IOException {
return new JsonAdapter<Object>() {
@Override public @Nullable Object fromJson(JsonReader reader) throws IOException {
reader.skipValue();
return defaultValue;
}
@Override
public void toJson(JsonWriter writer, @Nullable Object value) throws IOException {
throw new IOException("This method should never be called. "
+ "If you find this on your stacktraces please report it "
+ "to the https://github.com/square/moshi project");
@Override public void toJson(JsonWriter writer, Object value) throws IOException {
throw new IllegalArgumentException("Expected one of " + subtypes + " but found " + value
+ ", a " + value.getClass() + ". Register this subtype.");
}
};
}
@@ -295,13 +283,8 @@ public final class PolymorphicJsonAdapterFactory<T> implements JsonAdapter.Facto
int labelIndex = reader.selectString(labelOptions);
if (labelIndex == -1 && this.fallbackJsonAdapter == null) {
throw new JsonDataException("Expected one of "
+ labels
+ " for key '"
+ labelKey
+ "' but found '"
+ reader.nextString()
+ "'. Register a subtype for this label.");
throw new JsonDataException("Expected one of " + labels + " for key '" + labelKey
+ "' but found '" + reader.nextString() + "'. Register a subtype for this label.");
}
return labelIndex;
}
@@ -314,17 +297,11 @@ public final class PolymorphicJsonAdapterFactory<T> implements JsonAdapter.Facto
int labelIndex = subtypes.indexOf(type);
final JsonAdapter<Object> adapter;
if (labelIndex == -1) {
if (fallbackJsonAdapter == null || fallbackJsonAdapter instanceof DefaultJsonAdapter) {
throw new IllegalArgumentException("Expected one of "
+ subtypes
+ " but found "
+ value
+ ", a "
+ value.getClass()
+ ". Register this subtype.");
} else {
adapter = fallbackJsonAdapter;
if (fallbackJsonAdapter == null) {
throw new IllegalArgumentException("Expected one of " + subtypes + " but found " + value
+ ", a " + value.getClass() + ". Register this subtype.");
}
adapter = fallbackJsonAdapter;
} else {
adapter = jsonAdapters.get(labelIndex);
}

View File

@@ -23,11 +23,10 @@ import com.squareup.moshi.Moshi;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import javax.annotation.Nullable;
import okio.Buffer;
import org.junit.Test;
import javax.annotation.Nullable;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
@@ -114,15 +113,18 @@ public final class PolymorphicJsonAdapterFactoryTest {
.withSubtype(Success.class, "success")
.withSubtype(Error.class, "error")
.withFallbackJsonAdapter(new JsonAdapter<Object>() {
@Override
public Object fromJson(JsonReader reader) throws IOException {
reader.skipValue();
@Override public Object fromJson(JsonReader reader) throws IOException {
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("type");
assertThat(reader.nextString()).isEqualTo("data");
assertThat(reader.nextName()).isEqualTo("value");
assertThat(reader.nextString()).isEqualTo("Okay!");
reader.endObject();
return new EmptyMessage();
}
@Override
public void toJson(JsonWriter writer, @Nullable Object value) {
throw new RuntimeException("Not implemented as not needed for the test");
@Override public void toJson(JsonWriter writer, @Nullable Object value) {
throw new AssertionError();
}
})
)
@@ -185,14 +187,11 @@ public final class PolymorphicJsonAdapterFactoryTest {
.withSubtype(Success.class, "success")
.withSubtype(Error.class, "error")
.withFallbackJsonAdapter(new JsonAdapter<Object>() {
@Nullable
@Override
public Object fromJson(JsonReader reader) {
@Override public Object fromJson(JsonReader reader) {
throw new RuntimeException("Not implemented as not needed for the test");
}
@Override
public void toJson(JsonWriter writer, @Nullable Object value) throws IOException {
@Override public void toJson(JsonWriter writer, Object value) throws IOException {
writer.name("type").value("injected by fallbackJsonAdapter");
}
}))