2016-09-07 192 views
0

我正在嘗試使用Jackson與Kotlin反序列化Json字符串到類型爲OperationResult<String>的對象。如何從類型名稱中獲取類名?

我需要構建一個類型的對象,像這樣:

val mapper : ObjectMapper = ObjectMapper(); 
val type : JavaType = mapper.getTypeFactory() 
     .constructParametricType(*/ class of OperationResult */,, 
      /* class of String */); 
val result : OperationResult<String> = mapper.readValue(
        responseString, type); 

我試過以下,但他們沒有工作。

val type : JavaType = mapper.getTypeFactory() 
      .constructParametricType(
      javaClass<OperationResult>, 
      javaClass<String>); // Unresolved javaClass<T> 

val type : JavaType = mapper.getTypeFactory() 
      .constructParametricType(
      OperationResult::class, 
      String::class); 

如何從類型名稱中獲得java類?

+2

您應該使用Jackson-Kotiln模塊,然後不需要這些。 https://github.com/FasterXML/jackson-module-kotlin –

+0

@JaysonMinard謝謝。我已經下載並使用Maven構建了一個jar。我將繼續使用它。 –

+2

它在Maven Central中有工件,無需構建它... https://mvnrepository.com/artifact/com.fasterxml.jackson.module/jackson-module-kotlin/2.8.2 –

回答

3

預期以下工作:

val type = mapper.typeFactory.constructParametricType(OperationalResult::class.java, String::class.java) 
val operationalResult = mapper.readValue<OperationalResult<String>>("""{"result":"stack"}""", type) 
println(operationalResult.result) // -> stack 

一個簡單的替代使用com.fasterxml.jackson.core.type.TypeReference反序列化的泛型類型:

val operationalResult = mapper.readValue<OperationalResult<Double>>("""{"result":"5.5"}""", 
     object : TypeReference<OperationalResult<Double>>() {}) 
println(operationalResult.result) // -> 5.5 

而且隨着jackson-kotlin-module幫助下,你甚至可以寫:

val operationalResult = mapper.readValue<OperationalResult<Long>>("""{"result":"5"}""") 
println(operationalResult.result) 
+0

非常感謝。我正在使用的Eclipse的Kotlin插件不能解析'TypeReference'類型。它也提供很少或沒有智能感知來找到你想參考的類型所屬的地方。你能告訴我需要導入的包嗎? –

+0

@ WaterCoolerv2查看更新。 – miensol

+0

非常感謝。 :-) –

2

您需要獲取的實例不是KClass。爲了得到它,你只需使用::class.java而不是::class

val type : JavaType = mapper.typeFactory.constructParametricType(OperationResult::class.java, String::class.java) 
+0

謝謝。我實際上也嘗試過,但是一旦我輸入'OperationResult :: class.',intellisense就沒有提出任何建議。我相信這是Eclipse的Kotlin插件的一個問題,因爲它無法加載kotlin反射程序集。但我無法確定。我將IDE切換到IntelliJ IDEA Ultimate,如果您提出的解決方案有效,我會向您報告,我認爲這是正面的。再次感謝。 :-) –

+0

即使免費版本(Intellij IDEA社區)與Kotlin一起工作:) – rafal

+0

您是對的,但確實如此,但我想編寫Community Edition不支持的servlet。 http://stackoverflow.com/q/39072303/303685 –

4

當使用Jackson,GSON或其他實例化Kotlin對象的庫時,Kotlin有一些問題需要關注。一,你如何得到ClassTypeToken,TypeReference或某些圖書館想要了解的其他專業類。另一個是他們如何構造並不總是具有默認構造函數或不可變的類。

對於傑克遜來說,一個模塊專門用於涵蓋這些案例。在@ miensol的回答中提到了它。他示出了類似於一個例子:

import com.fasterxml.jackson.module.kotlin.* // added for clarity 

val operationalResult: OperationalResult<Long> = mapper.readValue(""{"result":"5"}""") 

這實際上是調用由科特林模塊添加到ObjectMapper一個內聯擴展函數,它使用斂物化泛型推斷結果的類型(可用於內聯函數)做任何事情來告訴傑克遜的數據類型。它會在幕後爲您創建一個Jackson TypeReference並將它傳遞給Jackson。這是函數的源:

inline fun <reified T: Any> ObjectMapper.readValue(content: String): T = readValue(content, object: TypeReference<T>() {}) 

您可以輕鬆地編寫相同,但該模塊具有較大數量的這些幫手做這項工作給你。此外,它還可以爲您調用非默認構造函數和靜態工廠方法。在Jackson 2.8。+中,它還可以更加智能地處理可空性和默認方法參數(允許在JSON中缺少值,因此使用默認值)。沒有這個模塊,你很快就會發現新的錯誤。

至於你使用mapper.typeFactory.constructParametricType你應該使用TypeReference來代替,它更容易並且遵循與上面相同的模式。

val myTypeRef = object: TypeReference<SomeOtherClass>() {} 

此代碼創建一個類(通過object expression)的匿名實例與指定的泛型類超型的TypeRefrence。然後Java反射可以查詢這些信息。

小心使用Class直接,因爲它會刪除泛型類型的信息,因此,使用SomeOtherClass::classSomeOtherClass::class.java所有失去的仿製藥,應當避免爲需要他們的知識的東西。

所以,即使你不使用Jackson-Kotlin模塊就可以避開一些事情,你很快就會遇到很多痛苦。這個模塊可以消除這些類型的錯誤,而且可以讓您以「Kotlin方式」執行更多操作,而不必去修改Kotlin。

+0

謝謝你這麼可愛和詳細的答案。我喜歡所有的解釋。我知道並且已經使用了對象聲明,對象表達式(用於創建Java的lambda表達式替代,例如.e.g。inline mouselisteners等)和伴隨對象。我明白當你說'對象時發生了什麼:TypeReference (){}'。不過,我真的很喜歡你解釋事物的方式。非常感謝您的寶貴時間。 :-) –