Kotlinx.Serializer - создайте быстрый JSON для отправки - PullRequest
3 голосов
/ 29 марта 2019

Я играл с Kotlinx.serialisation. Я пытался найти быстрый способ использовать Kotlinx.serialisation для создания простого простого JSON (в основном, чтобы отправить его), с минимальным беспорядком кода.

Для простой строки, такой как:

{"Album": "Foxtrot", "Year": 1972}

Я делал что-то вроде:

val str:String = Json.stringify(mapOf(
        "Album" to JsonPrimitive("Foxtrot"),
        "Year" to JsonPrimitive(1972)))

Что далеко не красиво. Мои элементы в основном примитивны, поэтому я хотел бы иметь что-то вроде:

val str:String = Json.stringify(mapOf(
   "Album" to "Sergeant Pepper",
   "Year" to 1967))

Кроме того, я был бы рад иметь решение с вложенным JSON. Что-то вроде:

Json.stringify(JsonObject("Movies", JsonArray(
   JsonObject("Name" to "Johnny English 3", "Rate" to 8),
   JsonObject("Name" to "Grease", "Rate" to 1))))

Это даст:

{
  "Movies": [
    {
      "Name":"Johnny English 3",
      "Rate":8
    },
    {
      "Name":"Grease",
      "Rate":1
    }
  ]
}

(не обязательно предварительно подтверждено, даже лучше нет)

Есть что-нибудь подобное?

Примечание : важно использовать сериализатор, а не прямую строку, такую ​​как

"""{"Name":$name, "Val": $year}"""

потому что опасно соединять строки. Любой незаконный символ может разрушить JSON! Я не хочу иметь дело с побегом нелегальных символов :-(

Спасибо

1 Ответ

2 голосов
/ 29 марта 2019

Дает ли этот набор методов расширения то, что вы хотите?

@ImplicitReflectionSerializer
fun Map<*, *>.toJson() = Json.stringify(toJsonObject())

@ImplicitReflectionSerializer
fun Map<*, *>.toJsonObject(): JsonObject = JsonObject(map {
    it.key.toString() to it.value.toJsonElement()
}.toMap())

@ImplicitReflectionSerializer
fun Any?.toJsonElement(): JsonElement = when (this) {
    null -> JsonNull
    is Number -> JsonPrimitive(this)
    is String -> JsonPrimitive(this)
    is Boolean -> JsonPrimitive(this)
    is Map<*, *> -> this.toJsonObject()
    is Iterable<*> -> JsonArray(this.map { it.toJsonElement() })
    is Array<*> -> JsonArray(this.map { it.toJsonElement() })
    else -> JsonPrimitive(this.toString()) // Or throw some "unsupported" exception?
}

Это позволяет вам передавать Map с различными типами ключей / значений в нем и возвращать его в JSON-представлении. На карте каждое значение может быть примитивом (строка, число или логическое значение), нулем, другой картой (представляющей дочерний узел в JSON) или массивом или коллекцией любого из вышеперечисленного.

Вы можете назвать это следующим образом:

val json = mapOf(
    "Album" to "Sergeant Pepper",
    "Year" to 1967,
    "TestNullValue" to null,
    "Musicians" to mapOf(
        "John" to arrayOf("Guitar", "Vocals"),
        "Paul" to arrayOf("Bass", "Guitar", "Vocals"),
        "George" to arrayOf("Guitar", "Sitar", "Vocals"),
        "Ringo" to arrayOf("Drums")
    )
).toJson()

Возвращает следующий JSON, без предварительного подтверждения, как вы хотели:

{"Album":"Sergeant Pepper","Year":1967,"TestNullValue":null,"Musicians":{"John":["Guitar","Vocals"],"Paul":["Bass","Guitar","Vocals"],"George":["Guitar","Sitar","Vocals"],"Ringo":["Drums"]}}

Возможно, вы также хотите добавить обработку для некоторых других типов, например, даты.

Но могу ли я просто проверить, что вы хотите вручную создать JSON в коде, а не создавать классы данных для всех ваших структур JSON и сериализовывать их таким образом? Я думаю, что это обычно более стандартный способ обработки такого рода вещей. Хотя, возможно, ваш вариант использования этого не позволяет.

Также ничего не стоит, чтобы код использовал аннотацию ImplicitReflectionSerializer, так как он использует отражение, чтобы выяснить, какой сериализатор использовать для каждого бита. Это все еще экспериментальная функциональность, которая может измениться в будущем.

...