/* * Copyright 2016 Serj Lotutovici * * 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.serjltt.moshi.adapters; import com.squareup.moshi.Json; import com.squareup.moshi.JsonAdapter; import com.squareup.moshi.JsonDataException; import com.squareup.moshi.JsonQualifier; import com.squareup.moshi.Moshi; import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; import org.junit.Ignore; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; public final class ElementAtJsonAdapterTest { // Lazy adapters work only within the context of moshi. private final Moshi moshi = new Moshi.Builder() .add(ElementAt.ADAPTER_FACTORY) .add(new Custom.CustomAdapter()) // We need to check that other annotations are not lost. .build(); @Test public void elementAt() throws Exception { JsonAdapter<Data> adapter = moshi.adapter(Data.class); Data fromJson = adapter.fromJson("{\n" + " \"obj\": [\n" + " \"one\",\n" + " \"two\"\n" + " ]\n" + "}"); assertThat(fromJson.str).isEqualTo("two"); String toJson = adapter.toJson(fromJson); // The excluded data is lost during parsing // Adapter under test assumes that the consumer doesn't need that data assertThat(toJson).isEqualTo("{\"obj\":[\"two\"]}"); } @Test public void fromJsonOnEmptyArrayReturnsNull() throws Exception { assertNullReturn("{\n" + " \"obj\": []\n" + "}"); } @Test public void fromJsonOnArrayOfSizeOneReturnsNull() throws Exception { assertNullReturn("{\n" + " \"obj\": [\"test\"]\n" + "}"); } @Test public void fromJsonOnNullArrayReturnsNull() throws Exception { assertNullReturn("{\n" + " \"obj\": null\n" + "}"); } @Test public void fromJsonExpectsAnArray() throws Exception { JsonAdapter<Data> adapter = moshi.adapter(Data.class); try { adapter.fromJson("{\n" + " \"obj\": \"this_will_throw\"\n" + "}"); fail(); } catch (JsonDataException e) { // Moshi's Collection adapter will throw assertThat(e).hasMessage("Expected BEGIN_ARRAY but was STRING at path $.obj"); } } // Currently there is no way to create an adapter @Test @Ignore public void factoryMaintainsOtherAnnotations() throws Exception { JsonAdapter<Data2> adapter = moshi.adapter(Data2.class); Data2 fromJson = adapter.fromJson("{\n" + " \"str\": [\n" + " \"test\"\n" + " ]\n" + "}"); assertThat(fromJson.str).isEqualTo("testCustom"); String toJson = adapter.toJson(fromJson); assertThat(toJson).isEqualTo("{\"str\":[\"test\"]}"); } // This one is redundant, but keeps JaCoCo quiet @Test public void factoryExpectsOnlyOneAnnotation() { // A list of fake annotations. Set<Annotation> annotations = new LinkedHashSet<Annotation>() { { add(new Annotation() { @Override public Class<? extends Annotation> annotationType() { return Test.class; } }); add(new Annotation() { @Override public Class<? extends Annotation> annotationType() { return Custom.class; } }); } }; assertThat(ElementAt.ADAPTER_FACTORY.create(String.class, annotations, moshi)).isNull(); // Emulate existing annotation (should also return null). annotations.add(new ElementAt() { @Override public Class<? extends Annotation> annotationType() { return ElementAt.class; } @Override public int index() { return 0; } }); assertThat(ElementAt.ADAPTER_FACTORY.create(String.class, annotations, moshi)).isNull(); } @Test public void toStringReflectsInnerAdapter() throws Exception { JsonAdapter<String> adapter = moshi.adapter(String.class, Collections.singleton(new ElementAt() { @Override public Class<? extends Annotation> annotationType() { return ElementAt.class; } @Override public int index() { return 2; } })); assertThat(adapter.toString()) .isEqualTo("JsonAdapter(String).nullSafe().collection().nullSafe().elementAt(2)"); } @Test public void elementAtDelegated() throws Exception { JsonAdapter<Data3> adapter = moshi.adapter(Data3.class); Data3 fromJson = adapter.fromJson("{\n" + " \"obj\": [\n" + " \"one\",\n" + " \"two\"\n" + " ]\n" + "}"); assertThat(fromJson.str).isEqualTo("two"); String toJson = adapter.toJson(fromJson); assertThat(toJson).isEqualTo("{\"obj\":[\"two\"]}"); } @JsonQualifier @ElementAt(index = 1) @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER }) @interface AlwaysElementAtIndexOne { } private void assertNullReturn(String string) throws IOException { JsonAdapter<Data> adapter = moshi.adapter(Data.class); Data fromJson = adapter.fromJson(string); assertThat(fromJson.str).isNull(); String toJson = adapter.toJson(fromJson); assertThat(toJson).isEqualTo("{\"obj\":[null]}"); } private static class Data { @ElementAt(index = 1) @Json(name = "obj") String str; } private static class Data2 { @ElementAt(index = 0) @Custom String str; } private static class Data3 { @AlwaysElementAtIndexOne @Json(name = "obj") String str; } }