Convert MapJsonAdapter to Kotlin (#1492)

* Rename .java to .kt

* Convert MapJsonAdapter to Kotlin

* Pull up factory into companion object

* Spotless

* apicmp

* Use knownNotNull

* Use a template
This commit is contained in:
Zac Sweers
2022-01-10 12:22:15 -05:00
committed by GitHub
parent 31b7a11027
commit d7b70637d2
6 changed files with 79 additions and 99 deletions

View File

@@ -35,7 +35,8 @@ val japicmp = tasks.register<JapicmpTask>("japicmp") {
"com.squareup.moshi.JsonAdapter#indent(java.lang.String)" // Was unintentionally open before
)
fieldExcludes = listOf(
"com.squareup.moshi.CollectionJsonAdapter#FACTORY" // False-positive, class is not public anyway
"com.squareup.moshi.CollectionJsonAdapter#FACTORY", // False-positive, class is not public anyway
"com.squareup.moshi.MapJsonAdapter#FACTORY" // Class is not public
)
}

View File

@@ -26,6 +26,7 @@ import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.NoSuchElementException;
import java.util.Set;
import javax.annotation.Nullable;
/**
* A map of comparable keys to values. Unlike {@code TreeMap}, this class uses insertion order for
@@ -90,7 +91,7 @@ final class LinkedHashTreeMap<K, V> extends AbstractMap<K, V> implements Seriali
}
@Override
public V put(K key, V value) {
public V put(K key, @Nullable V value) {
if (key == null) {
throw new NullPointerException("key == null");
}

View File

@@ -1,95 +0,0 @@
/*
* Copyright (C) 2015 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
*
* https://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 java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
/**
* Converts maps with string keys to JSON objects.
*
* <p>TODO: support maps with other key types and convert to/from strings.
*/
final class MapJsonAdapter<K, V> extends JsonAdapter<Map<K, V>> {
public static final Factory FACTORY =
new Factory() {
@Override
public @Nullable JsonAdapter<?> create(
Type type, Set<? extends Annotation> annotations, Moshi moshi) {
if (!annotations.isEmpty()) return null;
Class<?> rawType = Types.getRawType(type);
if (rawType != Map.class) return null;
Type[] keyAndValue = Types.mapKeyAndValueTypes(type, rawType);
return new MapJsonAdapter<>(moshi, keyAndValue[0], keyAndValue[1]).nullSafe();
}
};
private final JsonAdapter<K> keyAdapter;
private final JsonAdapter<V> valueAdapter;
MapJsonAdapter(Moshi moshi, Type keyType, Type valueType) {
this.keyAdapter = moshi.adapter(keyType);
this.valueAdapter = moshi.adapter(valueType);
}
@Override
public void toJson(JsonWriter writer, Map<K, V> map) throws IOException {
writer.beginObject();
for (Map.Entry<K, V> entry : map.entrySet()) {
if (entry.getKey() == null) {
throw new JsonDataException("Map key is null at " + writer.getPath());
}
writer.promoteValueToName();
keyAdapter.toJson(writer, entry.getKey());
valueAdapter.toJson(writer, entry.getValue());
}
writer.endObject();
}
@Override
public Map<K, V> fromJson(JsonReader reader) throws IOException {
LinkedHashTreeMap<K, V> result = new LinkedHashTreeMap<>();
reader.beginObject();
while (reader.hasNext()) {
reader.promoteNameToValue();
K name = keyAdapter.fromJson(reader);
V value = valueAdapter.fromJson(reader);
V replaced = result.put(name, value);
if (replaced != null) {
throw new JsonDataException(
"Map key '"
+ name
+ "' has multiple values at path "
+ reader.getPath()
+ ": "
+ replaced
+ " and "
+ value);
}
}
reader.endObject();
return result;
}
@Override
public String toString() {
return "JsonAdapter(" + keyAdapter + "=" + valueAdapter + ")";
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) 2015 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
*
* https://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 com.squareup.moshi.internal.knownNotNull
import java.lang.reflect.Type
/**
* Converts maps with string keys to JSON objects.
*
* TODO: support maps with other key types and convert to/from strings.
*/
internal class MapJsonAdapter<K, V>(moshi: Moshi, keyType: Type, valueType: Type) : JsonAdapter<Map<K, V?>>() {
private val keyAdapter: JsonAdapter<K> = moshi.adapter(keyType)
private val valueAdapter: JsonAdapter<V> = moshi.adapter(valueType)
override fun toJson(writer: JsonWriter, map: Map<K, V?>?) {
writer.beginObject()
// Never null because we wrap in nullSafe()
for ((key, value) in knownNotNull(map)) {
if (key == null) {
throw JsonDataException("Map key is null at " + writer.path)
}
writer.promoteValueToName()
keyAdapter.toJson(writer, key)
valueAdapter.toJson(writer, value)
}
writer.endObject()
}
override fun fromJson(reader: JsonReader): Map<K, V?> {
val result = LinkedHashTreeMap<K, V?>()
reader.beginObject()
while (reader.hasNext()) {
reader.promoteNameToValue()
val name = keyAdapter.fromJson(reader) ?: throw JsonDataException("Map key is null at ${reader.path}")
val value = valueAdapter.fromJson(reader)
val replaced = result.put(name, value)
if (replaced != null) {
throw JsonDataException(
"Map key '$name' has multiple values at path ${reader.path}: $replaced and $value"
)
}
}
reader.endObject()
return result
}
override fun toString() = "JsonAdapter($keyAdapter=$valueAdapter)"
companion object Factory : JsonAdapter.Factory {
override fun create(type: Type, annotations: Set<Annotation>, moshi: Moshi): JsonAdapter<*>? {
if (annotations.isNotEmpty()) return null
val rawType = type.rawType
if (rawType != Map::class.java) return null
val keyAndValue = Types.mapKeyAndValueTypes(type, rawType)
return MapJsonAdapter<Any, Any>(moshi, keyAndValue[0], keyAndValue[1]).nullSafe()
}
}
}

View File

@@ -366,7 +366,7 @@ public class Moshi internal constructor(builder: Builder) {
val BUILT_IN_FACTORIES: List<JsonAdapter.Factory> = buildList(6) {
add(StandardJsonAdapters)
add(CollectionJsonAdapter.Factory)
add(MapJsonAdapter.FACTORY)
add(MapJsonAdapter.Factory)
add(ArrayJsonAdapter.FACTORY)
add(RecordJsonAdapter.FACTORY)
add(ClassJsonAdapter.FACTORY)

View File

@@ -262,7 +262,7 @@ public final class MapJsonAdapterTest {
@SuppressWarnings("unchecked") // It's the caller's responsibility to make sure K and V match.
private <K, V> JsonAdapter<Map<K, V>> mapAdapter(Type keyType, Type valueType) {
return (JsonAdapter<Map<K, V>>)
MapJsonAdapter.FACTORY.create(
MapJsonAdapter.Factory.create(
Types.newParameterizedType(Map.class, keyType, valueType), NO_ANNOTATIONS, moshi);
}