Cit*_*sO2 3 generics kotlin moshi
(也在此处打开了一个问题:https : //github.com/square/moshi/issues/768但也被要求打开一个 stackoverflow 问题)我正在编写一个通用适配器来转换带有枚举值列表的 json 字符串。当列表包含不可用的枚举值时,枚举的标准适配器会引发异常。我想创建一个简单地跳过未知枚举值而不是抛出异常的适配器。我部分成功了,但由于某种原因,转换的对象不是 aList<Enum>而是 a 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)
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
和单元测试代码:
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)
}
}
Run Code Online (Sandbox Code Playgroud)
在调试第二个测试的 testJsonClass 对象时,我可以看到以下值(第一个测试也类似):

我认为这与 CollectionJsonAdapter 相关,因为自定义适配器是通过 CollectionJsonAdapter 调用的。在我认为我会将集合传递给转换器并编写jsonReader.beginArray()and 之前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;
}
Run Code Online (Sandbox Code Playgroud)
我不确定我能做些什么来解决这个问题,我不能在我的适配器中返回单个值,所以它需要是一个列表,但我也不知道如何强制 moshi 不使用 CollectionJsonAdapter 并传递整个集合到我的适配器。
将您的适配器注册为处理List<Tags>,而不是Tags.
.add(Types.newParameterizedType(List::class.java, Tags::class.java),
SkipNotFoundEnumInEnumListAdapter(Tags::class))
Run Code Online (Sandbox Code Playgroud)
此外,您需要在解码实现中添加 jsonReader.beingArray() 和 jsonReader.endArray() 调用。(请注意,您不需要@FromJson直接扩展JsonAdapter.)
override fun fromJson(jsonReader: JsonReader): List<T> {
val list = ArrayList<T>()
jsonReader.beginArray()
while(jsonReader.hasNext()){
val jsonNameValue = jsonReader.nextString()
val entry = jsonNameToEnum[jsonNameValue]
if(entry!= null){
list.add(entry)
}
}
jsonReader.endArray()
return list
}
Run Code Online (Sandbox Code Playgroud)
奖励:您可以使用 JsonReader.Options 优化 SkipNotFoundEnumInEnumListAdapter。
class SkipNotFoundEnumInEnumListAdapter<T : Enum<T>>(enumType: Class<T>) : JsonAdapter<List<T>>() {
private val nameStrings: Array<String>
private val constants: Array<T>
private val options: JsonReader.Options
init {
try {
constants = enumType.enumConstants
nameStrings = Array(constants.size) {
val constant = constants[it]
val annotation = enumType.getField(constant.name).getAnnotation(Json::class.java)
annotation?.name ?: constant.name
}
options = JsonReader.Options.of(*nameStrings)
} catch (e: NoSuchFieldException) {
throw AssertionError("Missing field in " + enumType.name, e)
}
}
@Throws(IOException::class)
override fun fromJson(reader: JsonReader): List<T> {
reader.beginArray()
val list = mutableListOf<T>()
while (reader.hasNext()) {
val index = reader.selectString(options)
if (index != -1) {
list += constants[index]
} else {
reader.skipValue()
}
}
reader.endArray()
return list
}
@Throws(IOException::class)
override fun toJson(writer: JsonWriter, value: List<T>?) {
if (value == null) {
throw IllegalArgumentException("Wrap in .nullSafe()")
}
writer.beginArray()
for (i in value.indices) {
writer.value(nameStrings[value[i].ordinal])
}
writer.endArray()
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3285 次 |
| 最近记录: |