Kotlin - группировка классов как запечатанный класс

клиент может запросить сервер таким образом (доступны только 3 типа значений, а длина может быть больше 3)

{
  data = [
  {
    "id": 1,
    "value": "string"
  },
{
    "id": 2,
  "value": {
    "address" : "1",
    "next" : "3",
  }
  },
{
    "id": 3,
  "value": {
    "count" : "1",
    "option" : "3",
  }
  },

и я хочу сгруппировать 3 вида типов значений строка / { "количество", "опция"} / {"адрес", "далее"}

data class Request(
  val data: SealedLikeClass,
)

как я могу определить SealedLikeClass ? Я не знаю, как включить строку в запечатанный класс

Вы пытаетесь сериализовать и десериализовать этот JSON? Или оба?

Sweeper 20.02.2023 12:41

оба !!!!!!!!!!

Really Joey 21.02.2023 03:49
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
50
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

Вы можете определить закрытую иерархию классов с помощью универсального Request<T>. Это ограничит параметр типа T точно из 3 упомянутых вами форм. Вы можете снять это ограничение, удалив ключевое слово sealed.

Так:

sealed interface Request<T> {
    val id: Int
    val value: T
}

data class StringRequest(
    override val id: Int, 
    override val value: String
) : Request<String>

data class Count(val count: String, val option: String)
data class CountRequest(
    override val id: Int, 
    override val value: Count
) : Request<Count>

data class Address(val address: String, val next: String)
data class AddressRequest(
    override val id: Int, 
    override val value: Address
) : Request<Address>

На самом деле, если вам не нужно sealed-ограничение, вам не нужны и другие Request-классы. В этом случае вы можете просто сделать это:

data class Request<T>(val id: Int, val value: T)

val aStringRequest = Request(5, "string")

data class Count(val count: String, val option: String)
val aCountRequest = Request(5, Count("count", "option"))

data class Address(val address: String, val next: String)
val anAddressRequest = Request(5, Address("address", "next"))

Изменить после комментария от OP:

Если вы хотите, чтобы только значение было запечатанным типом, вы не можете поддерживать случай, когда значение является строкой, потому что вы не можете заставить String расширить свой собственный пользовательский интерфейс. Если вы откажетесь от этого случая, вы можете сделать это:

data class Request(val key: String, val value: RequestValue)

sealed interface RequestValue
data class Count(val count: String, val option: String) : RequestValue
data class Address(val address: String, val next: String) : RequestValue

Я предполагаю, что подклассы Request были бы необходимы, если бы целью была десериализация JSON из сетевых ответов.

Joffrey 20.02.2023 12:44

Вероятно, модификатор sealed также понадобится, если OP не хочет прыгать через обручи, чтобы определить полиморфную десериализацию. По крайней мере, в kotlinx.serialization модификатора sealed может быть достаточно IIRC, но без sealed подклассы должны быть зарегистрированы вручную (или явно упомянуты на сайте вызова десериализации, но это не всегда возможно)

Joffrey 20.02.2023 12:46

Я хочу создать один класс DTO ``` класса данных RequestDto (ключ val: String, значение val: CommonClass) ```, но можно создать экземпляр запечатанного... возможно ли сгруппировать в CommonClass?

Really Joey 21.02.2023 03:31

Я обновил вопрос, кто-нибудь поможет?

Really Joey 21.02.2023 03:46

@ReallyJoey Это невозможно из-за первого примера, где значение представляет собой просто строку. Вы не можете изменить суперкласс String на CommonClass.

marstran 21.02.2023 09:31

@marstran Что делать, если строка игнорируется? Я имею в виду, что существуют только количество и адрес

Really Joey 21.02.2023 13:07

В настоящее время вы не можете определить запечатанный класс с существующими примитивными типами. Это потребовало бы объединения, но в настоящее время они не обозначаются в Котлине (см. https://youtrack.jetbrains.com/issue/KT-13108/Denotable-union-and-intersection-types).

В вашем случае похоже, что возможные типы для value не связаны и на самом деле не связаны друг с другом. В этом случае лучше всего, вероятно, использовать подход, основанный на дженериках , предложенный @marstran выше . Это полезно и хорошо работает, если вы уже знаете нужный вам подтип Request на сайте вызова.

Другой подход, по крайней мере, с использованием Kotlinx Serialization, заключается в том, чтобы определить тип как JsonElement, а затем отложить десериализацию на более позднее время (если вы только позже узнаете тип тела запроса).

Теперь, если свойство value представляет что-то конкретное, что может иметь разные формы (в отличие от общего и представления любого несвязанного типа тела запроса), вы можете определить свой собственный запечатанный класс для этого типа в Kotlin (возможно, с оболочкой для строки версия или что-то лучше), а затем добавьте собственный десериализатор JSON для вашего типа, который также может работать с простыми строками.

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