Как правильно использовать JSON.parse в kotlinjs с перечислениями? - PullRequest
0 голосов
/ 23 марта 2019

Во время моих новых приключений с kotlin-response я резко остановился, пытаясь проанализировать некоторые данные из моего бэкэнда, которые содержат значения enum.
Spring-Boot отправляет объект в форме JSON следующим образом:

{
  "id": 1,
  "username": "Johnny",
  "role": "CLIENT"
}

role в данном случае является значением enum и может иметь два значения CLIENT и LECTURER. Если бы я проанализировал это с помощью библиотеки java или разрешил это обработать Spring-Boot, role будет проанализирован с соответствующим значением перечисления.

С kotlin-js 'JSON.parse это не сработает, и у меня будет простое строковое значение.

После некоторого тестирования я придумал этот фрагмент

val json = """{
        "id": 1,
        "username": "Johnny",
        "role": "CLIENT",
    }"""

val member: Member = JSON.parse(json) { key: String, value: Any? ->
    if (key == "role") Member.Role.valueOf(value.toString())
    else value
}

, в котором мне нужно вручную определить преобразование из строкового значения в перечисление.

Что-то, чего мне не хватает, упростило бы это поведение?

(я не имею в виду использование идентификаторов для JSON, их поиск и т. Д. Мне любопытно, какой-то метод в Kotlin-JS)

У меня есть предположение, что нет, потому что "оригинал" JSON.parse в JS не делает этого, и Kotlin не добавляет туда ничего дополнительного, но у меня все еще есть надежда!

1 Ответ

5 голосов
/ 24 марта 2019

Насколько я знаю, нет.

Проблема

Kotlin.JS создает невероятно странную ситуацию типа при десериализации с использованием встроенного класса JSON, который на самом деле является зеркалом для класса JSON JavaScript . Хотя я не сделал много JavaScript, его тип обработки почти не существует. Только ручные броски могут применить его, поэтому JSON.parse не волнует, возвращает ли он SomeCustomObject или вновь созданный объект с точно такими же полями.

В качестве примера этого, если у вас есть два разных класса с одинаковыми именами полей (без наследования), и у вас есть функция, которая принимает переменную, не имеет значения, какой из них (или третий в этом отношении) он получает, пока существуют переменные, к которым он пытается получить доступ в классе.

Проблемы с типами проявляются в Kotlin. Теперь вернемся к Kotlin, рассмотрим этот код:

val json = """{
        "x": 1, "y": "yes", "z": {
            "x": 42, "y": 314159, "z": 444
        }
    }""".trimIndent()

data class SomeClass(val x: Int, val y: String, val z: Struct) 
data class Struct(val x: Int, val y: Int, val z: Int)

fun main(args: Array<String>) {
    val someInstance = JSON.parse<SomeClass>(json)
    if(someInstance.z::class != Struct::class) {
        println("Incompatible types: Required ${Struct::class}, found ${someInstance.z::class}");
    }
}

Что вы ожидаете, что это напечатает? Естественно было бы ожидать Struct. Тип также явно объявлен

К сожалению, это не так. Вместо этого он печатает:

Incompatible types: Required class Struct, found class Any

Точка

Встроенный JSON de / serializer не подходит для типов. Возможно, вы сможете исправить это с помощью другой библиотеки сериализации, но я не буду превращать это в «библиотеку use [this]».

По сути, JSON.parse не может выполнить синтаксический анализ объектов, как ожидалось. Если вы полностью удалите аргументы и попробуете необработанный JSON.parse(json); для JSON в своем вопросе, вы получите role, то есть String, а не Role, что вы можете ожидать. А JSON.parse не выполняет преобразование типов, что означает, что у вас есть два варианта: использовать библиотеку или использовать ваш подход.

Ваш подход, к сожалению, усложняется, если у вас есть вложенные объекты, но при изменении типов единственная опция, которую вы, похоже, оставили, это явный анализ объектов вручную.

TL; DR: Ваш подход в порядке.

...