(Здесь также открыта проблема: https://github.com/square/moshi/issues/768, но мне было предложено открыть также вопрос о переполнении стека) Я пишу универсальный адаптер для преобразования строки json со списками значений перечисления.Стандартный адаптер для перечислений генерирует исключение, когда список содержит значение перечисления, которое недоступно.Я хочу создать адаптер, который просто пропускает неизвестные значения перечисления, а не создает исключение.Мне это удалось частично, но по какой-то причине преобразованный объект был не List<Enum>
, а List<List<Enum>>
.
. Это адаптер, который я придумал:
package com.mytestcompany.appname.utils
import com.squareup.moshi.*
import kotlin.reflect.KClass
class SkipNotFoundEnumInEnumListAdapter<T : Enum<T>>(enumType: KClass<T>) : JsonAdapter<List<T>>(){
val jsonNameToEnum = HashMap<String,T>()
val enumToJsonName = HashMap<T,String>()
init{
val enumConstants = enumType.java.enumConstants
for(enumConstant in enumConstants){
val constantName = enumConstant.name
val jsonName = enumType.java.getField(constantName).getAnnotation(Json::class.java)
val lookupName = jsonName?.name ?: constantName
jsonNameToEnum[lookupName] = enumConstant
enumToJsonName[enumConstant] = lookupName
}
}
@FromJson
override fun fromJson(jsonReader: JsonReader): List<T>{
val list = ArrayList<T>()
while(jsonReader.hasNext()){
val jsonNameValue = jsonReader.nextString()
val entry = jsonNameToEnum[jsonNameValue]
if(entry!= null){
list.add(entry)
}
}
return list
}
@ToJson
override fun toJson(writer: JsonWriter, list: List<T>?){
if(list!=null){
for(item in list){
val jsonName = enumToJsonName[item]
if(jsonName != null){
writer.value(jsonName)
}
}
}
}
}
и код модульного теста:
package com.mytestcompany.appname.utils
import com.squareup.moshi.Json
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.junit.MockitoJUnitRunner
data class TestJsonClass(
val testJsonSubClass: TestJsonSubClass
)
data class TestJsonSubClass(
val tags: List<Tags>
)
enum class Tags {
@Json(name="tag1") TAG_1,
@Json(name="tag2") TAG_2,
@Json(name="tag3") TAG_3,
}
@RunWith(MockitoJUnitRunner::class)
class SkipNotFoundEnumInEnumListAdapterTest {
lateinit var jsonAdapter: JsonAdapter<TestJsonClass>
@Before
fun setUp() {
val moshi = Moshi.Builder()
.add(Tags::class.java, SkipNotFoundEnumInEnumListAdapter(Tags::class))
.add(KotlinJsonAdapterFactory())
.build()
jsonAdapter = moshi.adapter(TestJsonClass::class.java)
}
@Test
fun moshiAdapterKnownEnumsTest() {
val json = """
{
"testJsonSubClass": {
"tags": [
"tag1",
"tag2",
"tag3"
]
},
"validation": {}
}
""".trimIndent()
val testJsonClass = jsonAdapter.fromJson(json)
Assert.assertTrue(testJsonClass?.testJsonSubClass?.tags?.count() == 3)
}
@Test
fun moshiAdapterUnknownEnumsTest() {
val json = """
{
"testJsonSubClass": {
"tags": [
"tag1",
"tag2",
"tag5"
]
},
"validation": {}
}
""".trimIndent()
val testJsonClass = jsonAdapter.fromJson(json)
Assert.assertTrue(testJsonClass?.testJsonSubClass?.tags?.count() == 2)
}
}
При отладке объекта testJsonClass второго теста я вижу следующие значения (также похожие для первого теста): ![image](https://user-images.githubusercontent.com/3945602/49643150-409ba700-fa15-11e8-9820-c6bcab759f7f.png)
Я думаю, что это связано с CollectionJsonAdapter, потому что пользовательский адаптер вызывается через CollectionJsonAdapter.Прежде, чем я думал, я бы передал коллекцию конвертеру и написал jsonReader.beginArray()
и reader.endArray()
, но это уже было сделано для меня:
//in CollectionJsonAdapter.java
@Override public C fromJson(JsonReader reader) throws IOException {
C result = newCollection();
reader.beginArray();
while (reader.hasNext()) {
result.add(elementAdapter.fromJson(reader)); // calls the custom adapter
}
reader.endArray();
return result;
}
Я не уверен, что я могу сделать, чтобы решитьВ этом случае я не могу вернуть отдельные значения в моем адаптере, поэтому он должен быть списком, но я также не знаю, как заставить moshi не использовать CollectionJsonAdapter и передавать всю коллекцию моему адаптеру.