kotlin.serialization Я ведь правильно понимаю, что...
# russian
l
kotlin.serialization Я ведь правильно понимаю, что ни один из поддерживаемых протоколов не сможет "угадать" в какой именно класс надо десериализовывать объект если в классе указан только интерфейс?
Copy code
data class Test(
val test: SomeInterface
)
e
Cможет, если все фактические экземпляры сераилизуемы. Там всюду поддерживается “полиморфная” сериализация похожая на то, как это сделано в Jackson. Т.е. есть будет записан массив, где первый элемент это строчка — имя класса, а второй элемент это собственно объект.
👍 2
@sandwwraith Может рассказать больше. Планируется в будущем сделать для таких полей специальную аннотацию чтобы полиморфизм не включался по умолчанию (из соображений безопасности), а был исключительно opt-in
l
Да, он просто еще не онлайн, поэтому я тут спрашиваю. Это конечно круто, я не предполагал такого.
@elizarov А с CBOR/protobuf?
e
У CBOR такая же модель. Можно так же делать. В protobuf большого смысла нет, ибо без схемы его нормально не прочитать (там же только id лежать в пакете, нету имен полей)
l
Не получилось у меня с CBOR угадать в какой класс десериализовывать
Copy code
interface Test

@Serializable
data class A(var aa: String) : Test

@Serializable
data class B(var bb: String) : Test

@Serializable
data class container(
        val test: Test
)

val container = container(
                A("a") //Ошибка - пишет что нет сериализатора
        )

val dd = dump(container)
Вероятно я что то не так понял
e
С интерфейсами может и не работает пока. Если Test это абстрактный класс помеченный @Serializable, то должно работать.
l
И на js тоже?
e
Вроде да. @sandwwraith сможет точней сказать.
s
На JS не работает, потому что пока что нет “честного” способа по прочитанному класснейму сделать его инстанс на js рантайме
l
Ок, насоздаю полей под каждый тип
s
Так что к сожалению, полиморфизм работает пока что только на JVM
s
Т.е. есть будет записан массив, где первый элемент это строчка — имя класса, а второй элемент это собственно объект.
Почему именно массив а не просто поле
@type
или
@class
внутри json-объекта?
s
Парсер в целях перфоманса написан однопроходный, поэтому ему нужно знать тип до того, как он начал читать объект
s
Ну, мы тоже написали такой парсер. И он тоже однопроходный, и чатет поле
@type
. Просто реализация метода чтения внутренностей объекта вынесена а отдельный метод. Т.е. идет проверка, если первое поле
@type
, то читаем тип, а далее вызываем метод
readObjectRestContents
единственно условие - поле
@type
должно быть первым
Copy code
CxJsonElementType.BEGIN_OBJECT -> readObject {
      val nextKey = readNextKey(false)

      when {
      // string keyed map with first key escaped from keyword
        nextKey.startsWith("$") -> stringKeyedMap(readDynamicStringKeyedMapContents(nextKey.substring(1), linker))
      // reference, for example `{ "#class:field": "key value" }`
        nextKey.startsWith("#") -> entityReference(readDynamicReference(nextKey, linker))
      // keyword
        nextKey.startsWith("@") -> {
          val keyword = nextKey.substring(1)
          when (keyword) {
            "structure" -> {
              val valueType = readType(linker)
              structure(readJsonStructureContents(valueType as CxStructure<Any>, linker).instance)
            }
            "map" -> complexKeyedMap(readDynamicComplexKeyedMap(linker))
            "class" -> inlineEntity(readInlineEntityTypeAndContents<Any>(linker).instance)
            else -> error("unknown json @keyword: $nextKey, input: $this")
          }
        }
      // string keyed map
        else -> stringKeyedMap(readDynamicStringKeyedMapContents(nextKey, linker))
      }
    }
Планировали перейти на kotlinx.serialization когда он будет готов, но если типы будут записываться в виде массива, это будет не очень. Можно создать issue по этому поводу чтобы отслеживать?