Kotlin 科特林。尝试使用具体化类型来解析列表和数组
我试图在解析json时使用具体化类型。 它可以完美地处理单个json条目,但无法处理list 问题:Kotlin 科特林。尝试使用具体化类型来解析列表和数组,kotlin,kotlin-reified-type-parameters,Kotlin,Kotlin Reified Type Parameters,我试图在解析json时使用具体化类型。 它可以完美地处理单个json条目,但无法处理list 问题: String.parseList()方法中缺少什么 为什么ClassCastException会出现在.first()上,尽管赋值在前面通过了一行 包质量保证 导入com.fasterxml.jackson.databind.ObjectMapper 导入org.slf4j.LoggerFactory 导入org.testng.Assert 导入org.testng.annotations.Te
包质量保证
导入com.fasterxml.jackson.databind.ObjectMapper
导入org.slf4j.LoggerFactory
导入org.testng.Assert
导入org.testng.annotations.Test
类ReifiedParseListTest{
数据类用户(变量名称:String=“userName”,变量年龄:Int=0)
val log=LoggerFactory.getLogger(this.javaClass.name)
val objectMapper=objectMapper()
val json:String=“[{”name:“Alice”,“age:1},{”name:“Bob”,“age:2}]”
val应为:String=“[User(name=Alice,age=1),User(name=Bob,age=2)]”
inline fun String.parseList():List=objectMapper
.readValue(这个数组::class.java).toList()
@试验
有趣的检查列表_OK(){
val实际值:List=objectMapper
.readValue(json,数组::class.java).toList()
log.info(“actual.first()的类型为:{}”,actual.first().javaClass)
Assert.assertEquals(实际的.toString(),应为)
}
@试验
fun checkParseListReified_失败(){
val实际值:List=json.parseList()
Assert.assertEquals(实际的.toString(),应为)
//java.lang.AssertionError:
//预期:[用户(姓名=Alice,年龄=1),用户(姓名=Bob,年龄=2)]
//实际值:[{name=Alice,age=1},{name=Bob,age=2}]
}
@试验
fun checkParseListReifiedClassCast_失败(){
val实际值:List=json.parseList()
log.info(“actual.first()的类型为:{}”,actual.first().javaClass)
//java.lang.ClassCastException:java.util.LinkedHashMap不能强制转换为qa.ReifiedParseListTest$User
}
}
在这种情况下,具体化
有助于传播类型的类,但仍然存在类型擦除。为了避免这种情况,您可以使用类似于
JavaType
:
inline fun <reified V> String.parseList(): List<V> {
return objectMapper.readValue(this, objectMapper.getTypeFactory()
.constructCollectionType(List::class.java, V::class.java))
}
您会注意到它返回数组代码>,这是Kotlin说“这是来自Java的东西,我希望它能工作,但我不能保证”。现在,通过调用toList()
这会放松编译器,说“是的,最后我们返回一个Kotlin类型,它会好起来的”。但实际上,这是一个虚假的承诺
你得到的是Array
中填充了LinkedHashMap
,当它们被转换到用户时,当然会失败,因为我们给了编译器一个错误的承诺。它失败是因为Array::class.java
总是返回Array的类:
inline fun String.parseList():List=objectMapper
.readValue(这是Class.forName(“[L${V::Class.java.name};””)作为类。我最终得到了另一个解决方案,它似乎同时处理单个实体和列表
inline fun <reified V> String.parse(): V = objectMapper.readValue(this, object : TypeReference<V>() {})
@Test
fun checkParseSingle() {
val jsonSingle: String = """{"name":"Carol","age":3}"""
val expectedSingle: String = "User(name=Carol, age=3)"
val actual: User = jsonSingle.parse<User>()
Assert.assertEquals(actual.toString(), expectedSingle)
}
@Test
fun checkParseList() {
val jsonList: String = """[{"name":"Alice","age":1},{"name":"Bob","age":2}]"""
val expectedList: String = "[User(name=Alice, age=1), User(name=Bob, age=2)]"
val actual: List<User> = jsonList.parse<List<User>>()
Assert.assertEquals(actual.toString(), expectedList)
}
inline fun String.parse():V=objectMapper.readValue(此对象:TypeReference(){})
@试验
有趣的checkParseSingle(){
val jsonSingle:String=“{”name:“Carol”,“age”:3}”“”
val expectedSingle:String=“User(name=Carol,age=3)”
val实际值:User=jsonSingle.parse()
Assert.assertEquals(实际的.toString(),expectedSingle)
}
@试验
有趣的检查列表(){
val jsonList:String=“”[{“name”:“Alice”,“age:1},{“name”:“Bob”,“age:2}]”
val expectedList:String=“[User(name=Alice,age=1),User(name=Bob,age=2)]”
val实际值:List=jsonList.parse()
Assert.assertEquals(实际的.toString(),expectedList)
}
您需要捕获T:class.java
不会给出的泛型类型。但以下内容适用于任何泛型类型
inline fun <reified T> jacksonTypeRef(): TypeReference<T> = object: TypeReference<T>() {}
inline fun <reified T : Any> String.parseJson(): T {
return objectMapper.readValue(this, jacksonTypeRef<T>())
}
inlinejacksontyperef():TypeReference=object:TypeReference(){}
内联fun String.parseJson():T{
返回objectMapper.readValue(这是jacksonTypeRef())
}
这是Kotlin,所以在构造名称时,您可能应该使用Spring模板:Class.forName(“[L${V::Class.java.name};”)
有趣的技巧。因此,我必须在静态类型语言中使用字符串连接来正确解析类型……请记住这一点。
inline fun <reified V> String.parseList(): List<V> = objectMapper
.readValue(this, Class.forName("[L${V::class.java.name};") as Class<Array<V>>).toList()
inline fun <reified V> String.parse(): V = objectMapper.readValue(this, object : TypeReference<V>() {})
@Test
fun checkParseSingle() {
val jsonSingle: String = """{"name":"Carol","age":3}"""
val expectedSingle: String = "User(name=Carol, age=3)"
val actual: User = jsonSingle.parse<User>()
Assert.assertEquals(actual.toString(), expectedSingle)
}
@Test
fun checkParseList() {
val jsonList: String = """[{"name":"Alice","age":1},{"name":"Bob","age":2}]"""
val expectedList: String = "[User(name=Alice, age=1), User(name=Bob, age=2)]"
val actual: List<User> = jsonList.parse<List<User>>()
Assert.assertEquals(actual.toString(), expectedList)
}
inline fun <reified T> jacksonTypeRef(): TypeReference<T> = object: TypeReference<T>() {}
inline fun <reified T : Any> String.parseJson(): T {
return objectMapper.readValue(this, jacksonTypeRef<T>())
}