Кодируемый: сглаживание структур

Допустим, у меня есть некоторый тип, который имеет представление JSON как таковое:

{
  "count": 3,
  "name": "Pianos",
  "type": "instrument",
  "id": 1,
}

Допустим, я хочу представить это как объект Swift, который выглядит так:

struct SomeObject: Codable { // this is the object I'd like to represent
  let id: Int
  let details: SomeDetails
}

struct SomeDetails: Codable {
  let count: Int
  let name: String
  let type: String
}

Расшифровать этот объект не составляет труда. Но как будет работать кодирование в этом случае, чтобы я мог кодировать в плоскую структуру — ту же структуру, которую я использовал для создания этого объекта и поделился в приведенном выше примере JSON?

Стоит ли изучать 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
0
1 020
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

But how would encoding work in this instance?

Это просто работает:

struct SomeObject: Codable { 
    let id: Int
    let details: SomeDetails
}
struct SomeDetails: Codable {
    let count: Int
    let name: String
    let type: String
}
let someobject = SomeObject(id: 10, details: SomeDetails(count: 3, name: "ho", type: "hey"))
let json = try! JSONEncoder().encode(someobject)

Если вы настаиваете на искусственном сглаживании, просто напишите свой собственный encode(to:), например:

struct SomeObject: Codable {
    let id: Int
    let details: SomeDetails
    enum Keys : String, CodingKey {
        case id
        case count
        case name
        case type
    }
    func encode(to enc: Encoder) throws {
        var con = try enc.container(keyedBy: Keys.self)
        try con.encode(id, forKey: .id)
        try con.encode(details.count, forKey: .count)
        try con.encode(details.name, forKey: .name)
        try con.encode(details.type, forKey: .type)
    }
}
struct SomeDetails: Codable {
    let count: Int
    let name: String
    let type: String
}
let someobject = SomeObject(id: 10, details: SomeDetails(count: 3, name: "ho", type: "hey"))
let json = try! JSONEncoder().encode(someobject)

Разве кодировка в этом случае не вернет содержимое SomeDetails под ключом details? Цель состоит в том, чтобы получить плоскую структуру, точно такую ​​же, как структура, которая пришла.

wadda_wadda 21.03.2019 04:04

Я не знаю, что вы имеете в виду под всем этим. Структура нет «плоская». Этого не может быть. Это гнездо.

matt 21.03.2019 04:04

Идея состоит в том, что я хотел бы закодировать представление JSON с теми же ключами, которые я декодировал, — в приведенном выше примере будут ключи id и details. Я ищу json с ключами id, count, name, type. Другими словами, структура SomeDetails — это абстракция на стороне клиента, которая не должна существовать при кодировании объекта swift обратно в его представление данных.

wadda_wadda 21.03.2019 04:06

Тогда напишите свою encode(to:) реализацию. Вы должны знать, как это сделать, потому что вам, вероятно, пришлось бы написать init(from:), чтобы превратить плоский JSON во вложенные структуры.

matt 21.03.2019 04:13

Абсолютно, это правильный ответ. Я испытываю мозговой пердеж в данный момент. В любом случае, я собираюсь опубликовать свой ответ на этот вопрос на случай, если кому-то еще это понадобится в будущем.

wadda_wadda 21.03.2019 04:16

Если кто-то будет читать это в будущем (привет!), это просто вопрос шага в ваш сконструированный тип, который упаковывает значения, которые вы абстрагируете.

struct SomeDetails: Codable {
    let count: Int
    let name: String
    let type: String
}


struct SomeObject: Codable {
    let id: Int
    let details: SomeDetails

    enum CodingKeys: String, CodingKey {
        case id
    }

    enum DetailKeys: String, CodingKey {
        case count, name, type
    }

    init(from decoder: Decoder) throws {
        let topLevelContainer = try decoder.container(keyedBy: CodingKeys.self)
        let detailContainer = try decoder.container(keyedBy: DetailKeys.self)

        id = try topLevelContainer.decode(Int.self, forKey: .id)
        details = SomeDetails(
            count: try detailContainer.decode(Int.self, forKey: .count),
            name: try detailContainer.decode(String.self, forKey: .name),
            type: try detailContainer.decode(String.self, forKey: .type))
    }

    func encode(to encoder: Encoder) throws {
        var topLevelContainer = encoder.container(keyedBy: CodingKeys.self)
        try topLevelContainer.encode(id, forKey: .id)

        var detailContainer = encoder.container(keyedBy: DetailKeys.self)
        try detailContainer.encode(details.count, forKey: .count)
        try detailContainer.encode(details.name, forKey: .name)
        try detailContainer.encode(details.type, forKey: .type)
    }
}

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

matt 21.03.2019 04:20

Согласен, но это игрушечный пример; на самом деле это полезная нагрузка гигантский json в другом проблемном пространстве.

wadda_wadda 21.03.2019 04:41

Улучшение предыдущих ответов. Нет необходимости вручную кодировать или декодировать другие объекты, вы можете просто перенаправить инициализаторы.

struct SomeDetails: Codable {
  let count: Int
  let name: String
  let type: String
}

struct SomeObject: Codable {
  enum CodingKeys: String, CodingKey {
    case id
  }

  let id: Int
  let details: SomeDetails

  init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    id = try container.decode(Int.self, forKey: .id)
    details = try SomeDetails(from: decoder)
  }

  func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(id, forKey: .id)
    try details.encode(to: encoder)
  }
}

И для некоторого дополнительного сахара вы можете использовать динамический поиск членов Swift 5s, чтобы вы могли легко получить доступ к этим членам object.name.

@dynamicMemberLookup
struct SomeObject {
  ...

  subscript<T>(dynamicMember member: KeyPath<SomeDetails, T>) -> T {
    details[keyPath: member]
  }
}

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