【问题标题】:How can I remove certain elements from a list during json deserialization?如何在 json 反序列化期间从列表中删除某些元素?
【发布时间】:2019-12-12 16:26:51
【问题描述】:

假设我们有一个 api 调用,它为我提供了一个对象列表(例如 Person (name: String, surname: String, age: String)),并且对于服务的不一致(第一项中的 String 和第二项中的 Int)的响应是:

{
   "people":[
      {
         "name":"Jon",
         "surname":"Snow",
         "age":"34"
      },
      {
         "name":"Ted",
         "surname":"Mosby",
         "age":35
      }
   ]
}

是否可以使用自定义类型适配器/使用 gson 库的反序列化从列表中删除格式错误的元素? 如果是,最好的方法是什么?

【问题讨论】:

  • 哪个是格式错误的元素?您的 JSON 不是问题的最佳示例,因为反序列化模型类中的年龄是 Int 还是 String 应该没有任何困难。 Json 本身并没有声明它是某种特定类型,而是您将其反序列化为的类。我认为最好的方法取决于实际问题。使用JsonParser,您通常可以做任何事情,但这可能不是最优雅的方式,您可能只想将其用作最后的手段。

标签: android kotlin gson deserialization


【解决方案1】:

您可以通过使用自定义TypeAdapter 来实现这一点,因为它可以让您决定如何处理JsonReader 返回的令牌。

假设您有一个类型 Response 表示您在上面粘贴的 JSON 字符串(类似于 class Result(val people: List<Person>)),我认为这里的关键点是提供一个 TypeAdapter<List<Person>>,因为这样可以过滤掉无效的Person 对象 - 您不能简单地创建一个 TypeAdapter<Person> 来为无效的 Person 对象返回一个 null 值,因为 people 被定义为一个不可为空的对象列表(即 List<Person> 而不是 @ 987654332@.

上述的概念证明是:

data class Person(val name: String, val surname: String, val age: String)
class Result(val people: List<Person>)

fun main() {
    val json = """
        {
           "people":[
              {
                 "name":"Jon",
                 "surname":"Snow",
                 "age":"34"
              },
              {
                 "name":"Ted",
                 "surname":"Mosby",
                 "age":35
              },
              {
                 "name":"John",
                 "surname":"Smith",
                 "age": "40"
              }
           ]
        }""".trimIndent()

    val gson = GsonBuilder().registerTypeAdapterFactory(PersonListTypeAdapterFactory).create()

    val result = gson.fromJson(json, Result::class.java)
    println("Deserialized: ${result.people}")
    println("Serialized: ${gson.toJson(result)}")
}

object PersonListTypeAdapterFactory : TypeAdapterFactory {
    override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
        if (type.rawType != List::class.java || (type as? ParameterizedType)?.actualTypeArguments?.contains(Person::class.java) == false) {
            return null
        }
        val delegate: TypeAdapter<List<Person>> = gson.getDelegateAdapter(this, type) as TypeAdapter<List<Person>>
        return PersonListTypeAdapter(delegate) as TypeAdapter<T>
    }
}

class PersonListTypeAdapter(private val delegate: TypeAdapter<List<Person>>) : TypeAdapter<List<Person>>() {

    override fun write(out: JsonWriter, value: List<Person>) {
        delegate.write(out, value)
    }

    override fun read(input: JsonReader): List<Person> {
        input.beginArray()
        val people = mutableListOf<Person>()
        while (input.peek() != JsonToken.END_ARRAY) {
            val person = readPerson(input)
            if (person != null) {
                people.add(person)
            }
        }
        input.endArray()

        return people
    }

    private fun readPerson(input: JsonReader): Person? {
        input.beginObject()

        // initialising everything with nulls is a bit ugly
        var name: String? = null
        var surname: String? = null
        var age: String? = null

        var token = input.peek()
        while (token != JsonToken.END_OBJECT) {
            when (input.nextName()) {
                "name" -> name = input.nextString()
                "surname" -> surname = input.nextString()
                "age" -> {
                    token = input.peek()
                    age = if (token == JsonToken.STRING) {
                        input.nextString()
                    } else {
                        input.skipValue()
                        // logging if needed
                        null
                    }
                }
            }
            token = input.peek()
        }
        input.endObject()

        if (listOf(name, surname, age).none(Objects::isNull)) {
            return Person(name!!, surname!!, age!!)
        }
        return null
    }

}

该代码将打印:

Deserialized: [Person(name=Jon, surname=Snow, age=34), Person(name=John, surname=Smith, age=40)]
Serialized: {"people":[{"name":"Jon","surname":"Snow","age":"34"},{"name":"John","surname":"Smith","age":"40"}]}

我很确定还有很大的改进空间?

【讨论】:

    猜你喜欢
    • 2018-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-13
    • 2014-04-12
    相关资源
    最近更新 更多