Repository: square/moshi
Issue: Kotlin multiple @Transient fields

Having more than one @ Transient field is causing following exception.

```
Exception in thread "main" java.lang.IllegalArgumentException: duplicate option: [text=\\\\u0000"]
	at okio.Options.of(Options.java:66)
	at com.squareup.moshi.JsonReader$Options.of(JsonReader.java:538)
	at com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory.create(KotlinJsonAdapter.kt:260)
	at com.squareup.moshi.Moshi.adapter(Moshi.java:137)
	at com.squareup.moshi.Moshi.adapter(Moshi.java:97)
	at com.squareup.moshi.Moshi.adapter(Moshi.java:71)

```

Code to reproduce

```
data class TransientTest(
        val a: String,
        @Transient
        val b: String? = "b",
        @Transient
        val c: String? = "c"
)

fun main(args: Array<String>) {

    val adapter = Moshi.Builder()
            .add(KotlinJsonAdapterFactory())
            .build()
            .adapter(TransientTest::class.java)

    val test = TransientTest("a")

    adapter.toJson(test)
}
```

Changed files:
kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/codegen/GeneratedAdaptersTest.kt
kotlin/reflect/src/main/java/com/squareup/moshi/kotlin/reflect/KotlinJsonAdapter.kt
kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/reflect/KotlinJsonAdapterTest.kt

diff --git a/kotlin/reflect/src/main/java/com/squareup/moshi/kotlin/reflect/KotlinJsonAdapter.kt b/kotlin/reflect/src/main/java/com/squareup/moshi/kotlin/reflect/KotlinJsonAdapter.kt
index 555638b..b33a5ce 100644
--- a/kotlin/reflect/src/main/java/com/squareup/moshi/kotlin/reflect/KotlinJsonAdapter.kt
+++ b/kotlin/reflect/src/main/java/com/squareup/moshi/kotlin/reflect/KotlinJsonAdapter.kt
@@ -54,35 +54,36 @@ private val ABSENT_VALUE = Any()
  * constructor, and then by setting any additional properties that exist, if any.
  */
 internal class KotlinJsonAdapter<T>(
-  private val constructor: KFunction<T>,
-  private val bindings: List<Binding<T, Any?>?>,
-  private val options: JsonReader.Options
+  val constructor: KFunction<T>,
+  val allBindings: List<Binding<T, Any?>?>,
+  val nonTransientBindings: List<Binding<T, Any?>>,
+  val options: JsonReader.Options
 ) : JsonAdapter<T>() {
 
   override fun fromJson(reader: JsonReader): T {
     val constructorSize = constructor.parameters.size
 
     // Read each value into its slot in the array.
-    val values = Array<Any?>(bindings.size) { ABSENT_VALUE }
+    val values = Array<Any?>(allBindings.size) { ABSENT_VALUE }
     reader.beginObject()
     while (reader.hasNext()) {
       val index = reader.selectName(options)
-      val binding = if (index != -1) bindings[index] else null
-
-      if (binding == null) {
+      if (index == -1) {
         reader.skipName()
         reader.skipValue()
         continue
       }
+      val binding = nonTransientBindings[index]
 
-      if (values[index] !== ABSENT_VALUE) {
+      val propertyIndex = binding.propertyIndex
+      if (values[propertyIndex] !== ABSENT_VALUE) {
         throw JsonDataException(
             "Multiple values for '${binding.property.name}' at ${reader.path}")
       }
 
-      values[index] = binding.adapter.fromJson(reader)
+      values[propertyIndex] = binding.adapter.fromJson(reader)
 
-      if (values[index] == null && !binding.property.returnType.isMarkedNullable) {
+      if (values[propertyIndex] == null && !binding.property.returnType.isMarkedNullable) {
         throw Util.unexpectedNull(
             binding.property.name,
             binding.jsonName,
@@ -98,7 +99,7 @@ internal class KotlinJsonAdapter<T>(
         if (!constructor.parameters[i].type.isMarkedNullable) {
           throw Util.missingProperty(
               constructor.parameters[i].name,
-              bindings[i]?.jsonName,
+              allBindings[i]?.jsonName,
               reader
           )
         }
@@ -110,8 +111,8 @@ internal class KotlinJsonAdapter<T>(
     val result = constructor.callBy(IndexedParameterMap(constructor.parameters, values))
 
     // Set remaining properties.
-    for (i in constructorSize until bindings.size) {
-      val binding = bindings[i]!!
+    for (i in constructorSize until allBindings.size) {
+      val binding = allBindings[i]!!
       val value = values[i]
       binding.set(result, value)
     }
@@ -123,7 +124,7 @@ internal class KotlinJsonAdapter<T>(
     if (value == null) throw NullPointerException("value == null")
 
     writer.beginObject()
-    for (binding in bindings) {
+    for (binding in allBindings) {
       if (binding == null) continue // Skip constructor parameters that aren't properties.
 
       writer.name(binding.name)
@@ -135,11 +136,13 @@ internal class KotlinJsonAdapter<T>(
   override fun toString() = "KotlinJsonAdapter(${constructor.returnType})"
 
   data class Binding<K, P>(
-      val name: String,
-      val jsonName: String?,
-      val adapter: JsonAdapter<P>,
-      val property: KProperty1<K, P>,
-      val parameter: KParameter?) {
+    val name: String,
+    val jsonName: String?,
+    val adapter: JsonAdapter<P>,
+    val property: KProperty1<K, P>,
+    val parameter: KParameter?,
+    val propertyIndex: Int
+  ) {
     fun get(value: K) = property.get(value)
 
     fun set(result: K, value: P) {
@@ -257,7 +260,8 @@ class KotlinJsonAdapterFactory : JsonAdapter.Factory {
           jsonAnnotation?.name ?: name,
           adapter,
           property as KProperty1<Any, Any?>,
-          parameter
+          parameter,
+          parameter?.index ?: -1
       )
     }
 
@@ -271,9 +275,13 @@ class KotlinJsonAdapterFactory : JsonAdapter.Factory {
       bindings += binding
     }
 
-    bindings += bindingsByName.values
+    var index = bindings.size
+    for (bindingByName in bindingsByName) {
+      bindings += bindingByName.value.copy(propertyIndex = index++)
+    }
 
-    val options = JsonReader.Options.of(*bindings.map { it?.name ?: "\\u0000" }.toTypedArray())
-    return KotlinJsonAdapter(constructor, bindings, options).nullSafe()
+    val nonTransientBindings = bindings.filterNotNull()
+    val options = JsonReader.Options.of(*nonTransientBindings.map { it.name }.toTypedArray())
+    return KotlinJsonAdapter(constructor, bindings, nonTransientBindings, options).nullSafe()
   }
 }
diff --git a/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/codegen/GeneratedAdaptersTest.kt b/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/codegen/GeneratedAdaptersTest.kt
index 9466831..a583841 100644
--- a/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/codegen/GeneratedAdaptersTest.kt
+++ b/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/codegen/GeneratedAdaptersTest.kt
@@ -535,6 +535,22 @@ class GeneratedAdaptersTest {
   @JsonClass(generateAdapter = true)
   class TransientConstructorParameter(@Transient var a: Int = -1, var b: Int = -1)
 
+  @Test fun multipleTransientConstructorParameters() {
+    val moshi = Moshi.Builder().build()
+    val jsonAdapter = moshi.adapter(MultipleTransientConstructorParameters::class.java)
+
+    val encoded = MultipleTransientConstructorParameters(3, 5, 7)
+    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)
+    assertThat(decoded.c).isEqualTo(-1)
+  }
+
+  @JsonClass(generateAdapter = true)
+  class MultipleTransientConstructorParameters(@Transient var a: Int = -1, var b: Int = -1, @Transient var c: Int = -1)
+
   @Test fun transientProperty() {
     val moshi = Moshi.Builder().build()
     val jsonAdapter = moshi.adapter<TransientProperty>()
diff --git a/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/reflect/KotlinJsonAdapterTest.kt b/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/reflect/KotlinJsonAdapterTest.kt
index 0cb9dc2..739bad4 100644
--- a/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/reflect/KotlinJsonAdapterTest.kt
+++ b/kotlin/tests/src/test/kotlin/com/squareup/moshi/kotlin/reflect/KotlinJsonAdapterTest.kt
@@ -294,6 +294,21 @@ class KotlinJsonAdapterTest {
 
   class TransientConstructorParameter(@Transient var a: Int = -1, var b: Int = -1)
 
+  @Test fun multipleTransientConstructorParameters() {
+    val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
+    val jsonAdapter = moshi.adapter(MultipleTransientConstructorParameters::class.java)
+
+    val encoded = MultipleTransientConstructorParameters(3, 5, 7)
+    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)
+    assertThat(decoded.c).isEqualTo(-1)
+  }
+
+  class MultipleTransientConstructorParameters(@Transient var a: Int = -1, var b: Int = -1, @Transient var c: Int = -1)
+
   @Test fun requiredTransientConstructorParameterFails() {
     val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
     try {