KTor Gson DataConversion с интерфейсом

Я пытаюсь зарегистрировать интерфейс PubicKey для преобразования данных в KTor, чтобы легко получить такой открытый ключ:

data class StoreRequest(
    val publicKey: PublicKey
)
...
val publicKey: PublicKey = call.receive<StoreRequest>().publicKey

Для этого я использовал эту страницу: https://ktor.io/servers/features/data-conversion.html
Я зарегистрировал этот конвертер данных:

convert<PublicKey> {
    decode { values, _ ->
        // When I add a breakpoint here it won't be reached.
        values.singleOrNull()?.let { key ->
            val encryptedKey = Base64.getDecoder().decode(key.split(" ")[1])
            val inputStream = DataInputStream(ByteArrayInputStream(encryptedKey))

            val format = String(ByteArray(inputStream.readInt()).also(inputStream::readFully))

            if (format != "ssh-rsa") throw RuntimeException("Unsupported format")

            val publicExponent = ByteArray(inputStream.readInt()).also(inputStream::readFully)
            val modulus = ByteArray(inputStream.readInt()).also(inputStream::readFully)

            val spec = RSAPublicKeySpec(BigInteger(modulus), BigInteger(publicExponent))
            val keyFactory = KeyFactory.getInstance("RSA")

            keyFactory.generatePublic(spec)
        }
    }
}

Но по какой-то причине Gson жалуется, потому что я использую интерфейс:

java.lang.RuntimeException: Unable to invoke no-args constructor for interface java.security.PublicKey. Register an InstanceCreator with Gson for this type may fix this problem`

Итак, я предполагаю, что мне нужно создать InstanceCreator для начального значения. Это начальный класс PublicKey, который я создал:

class PkTest : PublicKey {
    override fun getAlgorithm(): String = ""
    override fun getEncoded(): ByteArray = ByteArray(0)
    override fun getFormat(): String = ""
}
...
install(ContentNegotiation) {
    gson {
        setPrettyPrinting()
        registerTypeAdapter(PublicKey::class.java, InstanceCreator<PublicKey> { PkTest() // This is called when I add a breakpoint })
    }
}

Но это тоже не работает! Это исключение, которое я получаю:

com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 2 column 16 path $.publicKey

По какой-то причине он ожидает, что предоставленный ключ будет объектом JSON, в то время как я предоставляю это:

{
    "publicKey": "ssh-rsa AAAAB3NzaC1yc2EABAADAQABAAACAQDBPL2s+25Ank3zS6iHUoVk0tS63dZM0LzAaniiDon0tdWwq4vcL4+fV8BsAEcpMeijS92JhDDc9FccXlHbdDcmd6c4ITOt9h9xxhIefGsi1FTVJ/EjVtbqF5m0bu7ruIMGvuP1p5s004roHx9y0UdHvD/yNWLISMhy4nio6jLailIj3FS53Emj1WRNsOrpja3LzPXzhuuj6YnD9yfByT7iGZipxkmleaXrknChPClLI9uhcqtAzBLdd0NVTJLOt/3+d1cSNwdBw9e53wJvpEmH+P8UOZd+oV/y7cHIej4jQpBXVvpJR1Yaluh5RuxY90B0hSescUAj5g/3HVPpR/gE7op6i9Ab//0iXF15uWGlGzipI4lA2/wYEtv8swTjmdCTMNcTDw/1huTDEzZjghIKVpskHde/Lj416c7eSByLqsMg2OhlZGChKznpIjhuNRXz93DwqKuIKvJKSnhqaJDxmDGfG7nlQ/eTwGeAZ6VR50yMPiRTIpuYd767+Nsg486z7p0pnKoBlL6ffTbfeolUX2b6Nb9ZIOxJdpCSNTQRKQ50p4Y3S580cUM1Y2EfjlfIQG1JdmTQYB75AZXi/cB2PvScmF0bXRoj7iHg4lCnSUvRprWA0xbwzCW/wjNqw6MyRX42FFlvSRrmfaxGZxKYbmk3TzBv+Fp+CADPqQm3OQ== [email protected]"
}

Как я могу "обмануть" GSON, приняв строку для PublicKey? Или я еще что-то делаю не так?

Я думаю, что GSON хочет сериализовать объект JSON в PublicKey. Но я хочу, чтобы он принимал String. Я думаю, это должно быть возможно, потому что такие классы, как UUID и Date, работают нормально ...

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
0
706
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я решил это! Вместо InstanceCreator я использовал JsonDeserializer:

install(ContentNegotiation) {
    gson {
        setPrettyPrinting()
        registerTypeAdapter(PublicKey::class.java, JsonDeserializer<PublicKey> { json, _, _ ->
            // TODO some type checking.
            val key = json.asString
            val encryptedKey = Base64.getDecoder().decode(key.split(" ")[1])
            val inputStream = DataInputStream(ByteArrayInputStream(encryptedKey))

            val format = String(ByteArray(inputStream.readInt()).also(inputStream::readFully))

            if (format != "ssh-rsa") throw RuntimeException("Unsupported format")

            val publicExponent = ByteArray(inputStream.readInt()).also(inputStream::readFully)
            val modulus = ByteArray(inputStream.readInt()).also(inputStream::readFully)

            val spec = RSAPublicKeySpec(BigInteger(modulus), BigInteger(publicExponent))
            val keyFactory = KeyFactory.getInstance("RSA")

            keyFactory.generatePublic(spec)
        })
    }
}

Эта часть полностью снимается:

convert<PublicKey> {
    decode { values, _ ->
        ...
    }
}

Другие вопросы по теме