(
+ val name: String,
+ val adapter: JsonAdapter,
+ val property: KProperty1,
+ val parameter: KParameter?) {
+ init {
+ if (property !is KMutableProperty1 && parameter == null) {
+ throw IllegalArgumentException("No constructor or var property for ${property.name}")
+ }
+ }
+
+ fun get(value: K) = property.get(value)
+
+ fun set(result: K, value: P) {
+ if (value !== ABSENT_VALUE) {
+ (property as KMutableProperty1).set(result, value)
+ }
+ }
+ }
+
+ /** A simple [Map] that uses parameter indexes instead of sorting or hashing. */
+ class IndexedParameterMap(val parameterKeys: List, val parameterValues: Array)
+ : AbstractMap() {
+
+ override val entries: Set>
+ get() {
+ val allPossibleEntries = parameterKeys.mapIndexed { index, value ->
+ SimpleEntry(value, parameterValues[index])
+ }
+ return allPossibleEntries.filterTo(LinkedHashSet>()) {
+ it.value !== ABSENT_VALUE
+ }
+ }
+
+ override fun containsKey(key: KParameter) = parameterValues[key.index] !== ABSENT_VALUE
+
+ override fun get(key: KParameter): Any? {
+ val value = parameterValues[key.index]
+ return if (value !== ABSENT_VALUE) value else null
+ }
+ }
+
+ companion object {
+ @JvmField val FACTORY = Factory { type, annotations, moshi ->
+ if (!annotations.isEmpty()) return@Factory null
+
+ val rawType = Types.getRawType(type)
+ val platformType = ClassJsonAdapter.isPlatformType(rawType)
+ if (platformType) return@Factory null
+
+ if (!rawType.isAnnotationPresent(KOTLIN_METADATA)) return@Factory null
+
+ val constructor = rawType.kotlin.primaryConstructor ?: return@Factory null
+ val parametersByName = constructor.parameters.associateBy { it.name }
+ constructor.isAccessible = true
+
+ val bindingsByName = LinkedHashMap>()
+
+ for (property in rawType.kotlin.memberProperties) {
+ if (Modifier.isTransient(property.javaField?.modifiers ?: 0)) continue
+
+ property.isAccessible = true
+ var allAnnotations = property.annotations
+ var jsonAnnotation = property.findAnnotation()
+
+ val parameter = parametersByName[property.name]
+ if (parameter != null) {
+ allAnnotations += parameter.annotations
+ if (jsonAnnotation == null) {
+ jsonAnnotation = parameter.findAnnotation()
+ }
+ }
+
+ val name = jsonAnnotation?.name ?: property.name
+ val adapter = moshi.adapter(
+ property.returnType.javaType, Util.jsonAnnotations(allAnnotations.toTypedArray()))
+
+ bindingsByName[property.name] =
+ Binding(name, adapter, property as KProperty1, parameter)
+ }
+
+ val bindings = ArrayList?>()
+
+ for (parameter in constructor.parameters) {
+ val binding = bindingsByName.remove(parameter.name)
+ if (binding == null && !parameter.isOptional) {
+ throw IllegalArgumentException(
+ "No property for required constructor parameter ${parameter.name}")
+ }
+ bindings += binding
+ }
+
+ bindings += bindingsByName.values
+
+ val options = JsonReader.Options.of(*bindings.map { it?.name ?: "\u0000" }.toTypedArray())
+ KotlinJsonAdapter(constructor, bindings, options)
+ }
+ }
+}
diff --git a/kotlin/src/test/java/com/squareup/moshi/KotlinJsonAdapterTest.kt b/kotlin/src/test/java/com/squareup/moshi/KotlinJsonAdapterTest.kt
new file mode 100644
index 0000000..0c67c9a
--- /dev/null
+++ b/kotlin/src/test/java/com/squareup/moshi/KotlinJsonAdapterTest.kt
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2017 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 org.assertj.core.api.Assertions.assertThat
+import org.junit.Assert.fail
+import org.junit.Test
+import java.io.ByteArrayOutputStream
+import java.util.Locale
+import java.util.SimpleTimeZone
+import kotlin.annotation.AnnotationRetention.RUNTIME
+
+class KotlinJsonAdapterTest {
+ @Test fun constructorParameters() {
+ val moshi = Moshi.Builder().add(KotlinJsonAdapter.FACTORY).build()
+ val jsonAdapter = moshi.adapter(ConstructorParameters::class.java)
+
+ val encoded = ConstructorParameters(3, 5)
+ assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}")
+
+ val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")
+ assertThat(decoded.a).isEqualTo(4)
+ assertThat(decoded.b).isEqualTo(6)
+ }
+
+ class ConstructorParameters(var a: Int, var b: Int)
+
+ @Test fun properties() {
+ val moshi = Moshi.Builder().add(KotlinJsonAdapter.FACTORY).build()
+ val jsonAdapter = moshi.adapter(Properties::class.java)
+
+ val encoded = Properties()
+ encoded.a = 3
+ encoded.b = 5
+ assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}")
+
+ val decoded = jsonAdapter.fromJson("{\"a\":3,\"b\":5}")
+ assertThat(decoded.a).isEqualTo(3)
+ assertThat(decoded.b).isEqualTo(5)
+ }
+
+ class Properties {
+ var a: Int = -1
+ var b: Int = -1
+ }
+
+ @Test fun constructorParametersAndProperties() {
+ val moshi = Moshi.Builder().add(KotlinJsonAdapter.FACTORY).build()
+ val jsonAdapter = moshi.adapter(ConstructorParametersAndProperties::class.java)
+
+ val encoded = ConstructorParametersAndProperties(3)
+ encoded.b = 5
+ assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}")
+
+ val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")
+ assertThat(decoded.a).isEqualTo(4)
+ assertThat(decoded.b).isEqualTo(6)
+ }
+
+ class ConstructorParametersAndProperties(var a: Int) {
+ var b: Int = -1
+ }
+
+ @Test fun immutableConstructorParameters() {
+ val moshi = Moshi.Builder().add(KotlinJsonAdapter.FACTORY).build()
+ val jsonAdapter = moshi.adapter(ImmutableConstructorParameters::class.java)
+
+ val encoded = ImmutableConstructorParameters(3, 5)
+ assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}")
+
+ val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")
+ assertThat(decoded.a).isEqualTo(4)
+ assertThat(decoded.b).isEqualTo(6)
+ }
+
+ class ImmutableConstructorParameters(val a: Int, val b: Int)
+
+ @Test fun immutableProperties() {
+ val moshi = Moshi.Builder().add(KotlinJsonAdapter.FACTORY).build()
+ val jsonAdapter = moshi.adapter(ImmutableProperties::class.java)
+
+ val encoded = ImmutableProperties(3, 5)
+ assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}")
+
+ val decoded = jsonAdapter.fromJson("{\"a\":3,\"b\":5}")
+ assertThat(decoded.a).isEqualTo(3)
+ assertThat(decoded.b).isEqualTo(5)
+ }
+
+ class ImmutableProperties(a: Int, b: Int) {
+ val a = a
+ val b = b
+ }
+
+ @Test fun constructorDefaults() {
+ val moshi = Moshi.Builder().add(KotlinJsonAdapter.FACTORY).build()
+ val jsonAdapter = moshi.adapter(ConstructorDefaultValues::class.java)
+
+ val encoded = ConstructorDefaultValues(3, 5)
+ assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}")
+
+ val decoded = jsonAdapter.fromJson("{\"b\":6}")
+ assertThat(decoded.a).isEqualTo(-1)
+ assertThat(decoded.b).isEqualTo(6)
+ }
+
+ class ConstructorDefaultValues(var a: Int = -1, var b: Int = -2)
+
+ @Test fun requiredValueAbsent() {
+ val moshi = Moshi.Builder().add(KotlinJsonAdapter.FACTORY).build()
+ val jsonAdapter = moshi.adapter(RequiredValueAbsent::class.java)
+
+ try {
+ jsonAdapter.fromJson("{\"a\":4}")
+ fail()
+ } catch(expected: JsonDataException) {
+ assertThat(expected).hasMessage("Required value b missing at $")
+ }
+ }
+
+ class RequiredValueAbsent(var a: Int = 3, var b: Int)
+
+ @Test fun duplicatedValue() {
+ val moshi = Moshi.Builder().add(KotlinJsonAdapter.FACTORY).build()
+ val jsonAdapter = moshi.adapter(DuplicateValue::class.java)
+
+ try {
+ jsonAdapter.fromJson("{\"a\":4,\"a\":4}")
+ fail()
+ } catch(expected: JsonDataException) {
+ assertThat(expected).hasMessage("Multiple values for a at $.a")
+ }
+ }
+
+ class DuplicateValue(var a: Int = -1, var b: Int = -2)
+
+ @Test fun explicitNull() {
+ val moshi = Moshi.Builder().add(KotlinJsonAdapter.FACTORY).build()
+ val jsonAdapter = moshi.adapter(ExplicitNull::class.java)
+
+ val encoded = ExplicitNull(null, 5)
+ assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"b\":5}")
+ assertThat(jsonAdapter.serializeNulls().toJson(encoded)).isEqualTo("{\"a\":null,\"b\":5}")
+
+ val decoded = jsonAdapter.fromJson("{\"a\":null,\"b\":6}")
+ assertThat(decoded.a).isEqualTo(null)
+ assertThat(decoded.b).isEqualTo(6)
+ }
+
+ class ExplicitNull(var a: Int?, var b: Int?)
+
+ // TODO(jwilson): if a nullable field is absent, just do the obvious thing instead of crashing?
+ @Test fun absentNull() {
+ val moshi = Moshi.Builder().add(KotlinJsonAdapter.FACTORY).build()
+ val jsonAdapter = moshi.adapter(AbsentNull::class.java)
+
+ val encoded = AbsentNull(null, 5)
+ assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"b\":5}")
+ assertThat(jsonAdapter.serializeNulls().toJson(encoded)).isEqualTo("{\"a\":null,\"b\":5}")
+
+ try {
+ jsonAdapter.fromJson("{\"b\":6}")
+ fail()
+ } catch(expected: JsonDataException) {
+ assertThat(expected).hasMessage("Required value a missing at $")
+ }
+ }
+
+ class AbsentNull(var a: Int?, var b: Int?)
+
+ @Test fun constructorParameterWithQualifier() {
+ val moshi = Moshi.Builder()
+ .add(KotlinJsonAdapter.FACTORY)
+ .add(UppercaseJsonAdapter())
+ .build()
+ val jsonAdapter = moshi.adapter(ConstructorParameterWithQualifier::class.java)
+
+ val encoded = ConstructorParameterWithQualifier("Android", "Banana")
+ assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":\"ANDROID\",\"b\":\"Banana\"}")
+
+ val decoded = jsonAdapter.fromJson("{\"a\":\"Android\",\"b\":\"Banana\"}")
+ assertThat(decoded.a).isEqualTo("android")
+ assertThat(decoded.b).isEqualTo("Banana")
+ }
+
+ class ConstructorParameterWithQualifier(@Uppercase var a: String, var b: String)
+
+ @Test fun propertyWithQualifier() {
+ val moshi = Moshi.Builder()
+ .add(KotlinJsonAdapter.FACTORY)
+ .add(UppercaseJsonAdapter())
+ .build()
+ val jsonAdapter = moshi.adapter(PropertyWithQualifier::class.java)
+
+ val encoded = PropertyWithQualifier()
+ encoded.a = "Android"
+ encoded.b = "Banana"
+ assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":\"ANDROID\",\"b\":\"Banana\"}")
+
+ val decoded = jsonAdapter.fromJson("{\"a\":\"Android\",\"b\":\"Banana\"}")
+ assertThat(decoded.a).isEqualTo("android")
+ assertThat(decoded.b).isEqualTo("Banana")
+ }
+
+ class PropertyWithQualifier {
+ @Uppercase var a: String = ""
+ var b: String = ""
+ }
+
+ @Test fun constructorParameterWithJsonName() {
+ val moshi = Moshi.Builder().add(KotlinJsonAdapter.FACTORY).build()
+ val jsonAdapter = moshi.adapter(ConstructorParameterWithJsonName::class.java)
+
+ val encoded = ConstructorParameterWithJsonName(3, 5)
+ assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"key a\":3,\"b\":5}")
+
+ val decoded = jsonAdapter.fromJson("{\"key a\":4,\"b\":6}")
+ assertThat(decoded.a).isEqualTo(4)
+ assertThat(decoded.b).isEqualTo(6)
+ }
+
+ class ConstructorParameterWithJsonName(@Json(name = "key a") var a: Int, var b: Int)
+
+ @Test fun propertyWithJsonName() {
+ val moshi = Moshi.Builder().add(KotlinJsonAdapter.FACTORY).build()
+ val jsonAdapter = moshi.adapter(PropertyWithJsonName::class.java)
+
+ val encoded = PropertyWithJsonName()
+ encoded.a = 3
+ encoded.b = 5
+ assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"key a\":3,\"b\":5}")
+
+ val decoded = jsonAdapter.fromJson("{\"key a\":4,\"b\":6}")
+ assertThat(decoded.a).isEqualTo(4)
+ assertThat(decoded.b).isEqualTo(6)
+ }
+
+ class PropertyWithJsonName {
+ @Json(name = "key a") var a: Int = -1
+ var b: Int = -1
+ }
+
+ @Test fun transientConstructorParameter() {
+ val moshi = Moshi.Builder().add(KotlinJsonAdapter.FACTORY).build()
+ val jsonAdapter = moshi.adapter(TransientConstructorParameter::class.java)
+
+ val encoded = TransientConstructorParameter(3, 5)
+ assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"b\":5}")
+
+ val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")
+ assertThat(decoded.a).isEqualTo(-1)
+ assertThat(decoded.b).isEqualTo(6)
+ }
+
+ class TransientConstructorParameter(@Transient var a: Int = -1, var b: Int = -1)
+
+ @Test fun transientProperty() {
+ val moshi = Moshi.Builder().add(KotlinJsonAdapter.FACTORY).build()
+ val jsonAdapter = moshi.adapter(TransientProperty::class.java)
+
+ val encoded = TransientProperty()
+ encoded.a = 3
+ encoded.b = 5
+ assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"b\":5}")
+
+ val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")
+ assertThat(decoded.a).isEqualTo(-1)
+ assertThat(decoded.b).isEqualTo(6)
+ }
+
+ class TransientProperty {
+ @Transient var a: Int = -1
+ var b: Int = -1
+ }
+
+ @Test fun supertypeConstructorParameters() {
+ val moshi = Moshi.Builder().add(KotlinJsonAdapter.FACTORY).build()
+ val jsonAdapter = moshi.adapter(SubtypeConstructorParameters::class.java)
+
+ val encoded = SubtypeConstructorParameters(3, 5)
+ assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}")
+
+ val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")
+ assertThat(decoded.a).isEqualTo(4)
+ assertThat(decoded.b).isEqualTo(6)
+ }
+
+ open class SupertypeConstructorParameters(var a: Int)
+
+ class SubtypeConstructorParameters(a: Int, var b: Int) : SupertypeConstructorParameters(a)
+
+ @Test fun supertypeProperties() {
+ val moshi = Moshi.Builder().add(KotlinJsonAdapter.FACTORY).build()
+ val jsonAdapter = moshi.adapter(SubtypeProperties::class.java)
+
+ val encoded = SubtypeProperties()
+ encoded.a = 3
+ encoded.b = 5
+ assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"b\":5,\"a\":3}")
+
+ val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")
+ assertThat(decoded.a).isEqualTo(4)
+ assertThat(decoded.b).isEqualTo(6)
+ }
+
+ open class SupertypeProperties {
+ var a: Int = -1
+ }
+
+ class SubtypeProperties : SupertypeProperties() {
+ var b: Int = -1
+ }
+
+ @Test fun extendsPlatformClassWithPrivateField() {
+ val moshi = Moshi.Builder().add(KotlinJsonAdapter.FACTORY).build()
+ val jsonAdapter = moshi.adapter(ExtendsPlatformClassWithPrivateField::class.java)
+
+ val encoded = ExtendsPlatformClassWithPrivateField(3)
+ assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3}")
+
+ val decoded = jsonAdapter.fromJson("{\"a\":4,\"id\":\"B\"}")
+ assertThat(decoded.a).isEqualTo(4)
+ assertThat(decoded.id).isEqualTo("C")
+ }
+
+ internal class ExtendsPlatformClassWithPrivateField(var a: Int) : SimpleTimeZone(0, "C")
+
+ @Test fun extendsPlatformClassWithProtectedField() {
+ val moshi = Moshi.Builder().add(KotlinJsonAdapter.FACTORY).build()
+ val jsonAdapter = moshi.adapter(ExtendsPlatformClassWithProtectedField::class.java)
+
+ val encoded = ExtendsPlatformClassWithProtectedField(3)
+ assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"buf\":[0,0],\"count\":0}")
+
+ val decoded = jsonAdapter.fromJson("{\"a\":4,\"buf\":[0,0],\"size\":0}")
+ assertThat(decoded.a).isEqualTo(4)
+ assertThat(decoded.buf()).isEqualTo(ByteArray(2, { 0 }))
+ assertThat(decoded.count()).isEqualTo(0)
+ }
+
+ internal class ExtendsPlatformClassWithProtectedField(var a: Int) : ByteArrayOutputStream(2) {
+ fun buf() = buf
+ fun count() = count
+ }
+
+ @Test fun platformTypeThrows() {
+ val moshi = Moshi.Builder().add(KotlinJsonAdapter.FACTORY).build()
+ try {
+ moshi.adapter(Triple::class.java)
+ fail()
+ } catch (e: IllegalArgumentException) {
+ assertThat(e).hasMessage("Platform class kotlin.Triple annotated [] "
+ + "requires explicit JsonAdapter to be registered")
+ }
+ }
+
+ @Test fun privateConstructorParameters() {
+ val moshi = Moshi.Builder().add(KotlinJsonAdapter.FACTORY).build()
+ val jsonAdapter = moshi.adapter(PrivateConstructorParameters::class.java)
+
+ val encoded = PrivateConstructorParameters(3, 5)
+ assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}")
+
+ val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")
+ assertThat(decoded.a()).isEqualTo(4)
+ assertThat(decoded.b()).isEqualTo(6)
+ }
+
+ class PrivateConstructorParameters(private var a: Int, private var b: Int) {
+ fun a() = a
+ fun b() = b
+ }
+
+ @Test fun privateProperties() {
+ val moshi = Moshi.Builder().add(KotlinJsonAdapter.FACTORY).build()
+ val jsonAdapter = moshi.adapter(PrivateProperties::class.java)
+
+ val encoded = PrivateProperties()
+ encoded.a(3)
+ encoded.b(5)
+ assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}")
+
+ val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")
+ assertThat(decoded.a()).isEqualTo(4)
+ assertThat(decoded.b()).isEqualTo(6)
+ }
+
+ class PrivateProperties {
+ var a: Int = -1
+ var b: Int = -1
+
+ fun a() = a
+
+ fun a(a: Int) {
+ this.a = a
+ }
+
+ fun b() = b
+
+ fun b(b: Int) {
+ this.b = b
+ }
+ }
+
+ // TODO(jwilson): resolve generic types?
+ // TODO(jwilson): inaccessible constructors?
+ // TODO(jwilson): constructors parameter that is not a property
+
+ @Retention(RUNTIME)
+ @JsonQualifier
+ annotation class Uppercase
+
+ class UppercaseJsonAdapter {
+ @ToJson fun toJson(@Uppercase s: String) : String {
+ return s.toUpperCase(Locale.US)
+ }
+ @FromJson @Uppercase fun fromJson(s: String) : String {
+ return s.toLowerCase(Locale.US)
+ }
+ }
+}
diff --git a/moshi/src/main/java/com/squareup/moshi/ClassJsonAdapter.java b/moshi/src/main/java/com/squareup/moshi/ClassJsonAdapter.java
index 377efa9..e364bb6 100644
--- a/moshi/src/main/java/com/squareup/moshi/ClassJsonAdapter.java
+++ b/moshi/src/main/java/com/squareup/moshi/ClassJsonAdapter.java
@@ -105,19 +105,6 @@ final class ClassJsonAdapter extends JsonAdapter {
}
}
- /**
- * Returns true if {@code rawType} is built in. We don't reflect on private fields of platform
- * types because they're unspecified and likely to be different on Java vs. Android.
- */
- private boolean isPlatformType(Class> rawType) {
- String name = rawType.getName();
- return name.startsWith("android.")
- || name.startsWith("java.")
- || name.startsWith("javax.")
- || name.startsWith("kotlin.")
- || name.startsWith("scala.");
- }
-
/** Returns true if fields with {@code modifiers} are included in the emitted JSON. */
private boolean includeField(boolean platformType, int modifiers) {
if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) return false;
@@ -125,6 +112,19 @@ final class ClassJsonAdapter extends JsonAdapter {
}
};
+ /**
+ * Returns true if {@code rawType} is built in. We don't reflect on private fields of platform
+ * types because they're unspecified and likely to be different on Java vs. Android.
+ */
+ static boolean isPlatformType(Class> rawType) {
+ String name = rawType.getName();
+ return name.startsWith("android.")
+ || name.startsWith("java.")
+ || name.startsWith("javax.")
+ || name.startsWith("kotlin.")
+ || name.startsWith("scala.");
+ }
+
private final ClassFactory classFactory;
private final FieldBinding>[] fieldsArray;
private final JsonReader.Options options;
diff --git a/moshi/src/main/java/com/squareup/moshi/Json.java b/moshi/src/main/java/com/squareup/moshi/Json.java
index 908fa45..0da3be9 100644
--- a/moshi/src/main/java/com/squareup/moshi/Json.java
+++ b/moshi/src/main/java/com/squareup/moshi/Json.java
@@ -19,12 +19,23 @@ import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
-/** Customizes how a field is encoded as JSON. */
-@Target({FIELD, METHOD})
+/**
+ * Customizes how a field is encoded as JSON.
+ *
+ * Although this annotation doesn't declare a {@link Target}, it is only honored in the following
+ * elements:
+ *
+ *
+ * - Java class fields
+ *
- Kotlin properties for use with {@code moshi-kotlin}. This includes both
+ * properties declared in the constructor and properties declared as members.
+ *
+ *
+ * Users of the AutoValue: Moshi
+ * Extension may also use this annotation on abstract getters.
+ */
@Retention(RUNTIME)
@Documented
public @interface Json {
diff --git a/moshi/src/test/java/com/squareup/moshi/MoshiTest.java b/moshi/src/test/java/com/squareup/moshi/MoshiTest.java
index ccde79b..e0055c4 100644
--- a/moshi/src/test/java/com/squareup/moshi/MoshiTest.java
+++ b/moshi/src/test/java/com/squareup/moshi/MoshiTest.java
@@ -533,15 +533,6 @@ public final class MoshiTest {
}
@Test public void addNullFails() throws Exception {
- JsonAdapter jsonAdapter = new JsonAdapter() {
- @Override public Object fromJson(JsonReader reader) throws IOException {
- throw new AssertionError();
- }
-
- @Override public void toJson(JsonWriter writer, Object value) throws IOException {
- throw new AssertionError();
- }
- };
Type type = Object.class;
Class extends Annotation> annotation = Annotation.class;
Moshi.Builder builder = new Moshi.Builder();
diff --git a/pom.xml b/pom.xml
index 09016e4..3f7af26 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,14 +21,16 @@
moshi
examples
adapters
+ kotlin
UTF-8
1.7
+ 1.1.1
- 1.11.0
+ 1.12.0
4.12
@@ -71,6 +73,22 @@
assertj-core
${assertj.version}
+
+ org.jetbrains.kotlin
+ kotlin-stdlib
+ ${kotlin.version}
+
+
+ org.jetbrains.kotlin
+ kotlin-reflect
+ ${kotlin.version}
+
+
+ org.jetbrains.kotlin
+ kotlin-test
+ ${kotlin.version}
+ test
+
@@ -100,6 +118,11 @@
+
+ org.jetbrains.kotlin
+ kotlin-maven-plugin
+ ${kotlin.version}
+