Я пытаюсь десериализовать строку Json в объект типа OperationResult<String>, используя Джексон с Kotlin.

Мне нужно создать объект типа так:

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 из имен типов?

1
Water Cooler v2 7 Сен 2016 в 21:40

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)
3
miensol 7 Сен 2016 в 20:07

Вам нужно получить экземпляр Class, а не KClass. Чтобы получить его, просто используйте ::class.java вместо ::class.

val type : JavaType = mapper.typeFactory.constructParametricType(OperationResult::class.java, String::class.java)
4
rafal 7 Сен 2016 в 19:33

У Kotlin есть несколько вещей, которые вызывают беспокойство при использовании Jackson, GSON или других библиотек, которые создают экземпляры объектов Kotlin. Во-первых, как получить Class, TypeToken, TypeReference или другой специализированный класс, о котором хотят знать некоторые библиотеки. Другой - как они могут создавать классы, которые не всегда имеют конструкторы по умолчанию или являются неизменяемыми.

Для Джексона был построен модуль специально для таких случаев. Это упоминается в ответе @miensol. Он показывает пример, похожий на:

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

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

На самом деле это вызывает встроенную функцию расширения, добавленную к ObjectMapper модулем Kotlin, и он использует предполагаемый тип результата, захватывая обобщенные универсальные шаблоны (доступные встроенным функциям), чтобы делать все необходимое, чтобы сообщить Джексону о данных. тип. Он создает для вас Джексона TypeReference за кулисами и передает его Джексону. Это источник функции:

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

Вы можете легко кодировать то же самое, но в модуле есть большее количество этих помощников, которые сделают эту работу за вас. Кроме того, он обрабатывает возможность вызывать нестандартные конструкторы и статические фабричные методы. А в Jackson 2.8. + Он также может более разумно обрабатывать параметры метода по умолчанию и допускать значение NULL (позволяя значениям отсутствовать в JSON и, следовательно, использовать значение по умолчанию). Без модуля вы скоро обнаружите новые ошибки.

Что касается использования mapper.typeFactory.constructParametricType, вы должны использовать вместо него TypeReference, это намного проще и следует тому же шаблону, что и выше.

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

Этот код создает анонимный экземпляр класса (через выражение объекта ) с супертипом TypeRefrence с указанным универсальным классом. Затем отражение Java может запросить эту информацию.

Будьте осторожны при прямом использовании Class, поскольку при этом стирается информация об общем типе, поэтому использование SomeOtherClass::class или SomeOtherClass::class.java теряет универсальные типы, и его следует избегать для вещей, требующих их знания.

Так что даже если вам удастся кое-что сойти с рук без использования модуля Jackson-Kotlin, вы скоро столкнетесь с большой болью позже. Вместо того, чтобы искажать ваш Kotlin, этот модуль удаляет эти типы ошибок и позволяет вам делать больше "способом Kotlin".

4
Jayson Minard 7 Сен 2016 в 22:40