Использование Codable parse Вложенный JSON, как ObjectMapper Swift 4 - PullRequest
0 голосов
/ 24 мая 2018

У меня есть строка JSON, я хочу проанализировать ее как ObjectMapper Using Codable Protocol.

    struct Health: Mappable {
    var size: [String : Any] = [:]?
    var name: Double?

    init?(map: Map) {

    }

    mutating func mapping(map: Map) {
        size    <- map["health.size"]
        name    <- map["health.name"]
    }
}

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

let jsonString = """
 {
    "health": {
        "size":{
            "width":150,
            "height":150
            },
        "name":"Apple"
        }
 }
 """ 

Я хочу получить доступ к свойствам с помощью (.) Оператора Dot, например health.size, без создания Struct Model for Health.

struct HealthType: Codable {
    var health: Health
 }

struct Health: Codable {
        var title: String
        var size: Size

        enum CodingKeys: String, CodingKey
        {
            case title = "name"
        }
     }

     struct Size: Codable {
        var width: Double
        var height: Double
     }

Ответы [ 4 ]

0 голосов
/ 27 мая 2018

Эй, я создал KeyedCodable , и я думаю, что это именно то, что вы ищете.Ваша реализация будет выглядеть следующим образом.

struct Health: Codable, Keyedable {
    var size: [String: Int]!
    var name: String?

    mutating func map(map: KeyMap) throws {
        try size <-> map["health.size"]
        try name <-> map["health.name"]
    }

    init(from decoder: Decoder) throws {
        try KeyedDecoder(with: decoder).decode(to: &self)
    }
}
0 голосов
/ 25 мая 2018

Если ваше намерение не раскрывать свойства, которые мне не нужны , то один из способов - это частное декодирование всей структуры, а затем сделать доступными только те свойства, которые вы собираетесьразоблачить во внешнем мире.

struct Health {
    let size: Size
    let title: String

    struct Size: Decodable {
        let width: Int
        let height: Int
    }
    private struct RawResponse: Decodable {
        let health: PrivateHealth
        struct PrivateHealth: Decodable {
            let size: Size
            let name: String
        }
    }
}

// Decodable requirement is moved to extension so that default initializer is accessible
extension Health: Decodable {
    init(from decoder: Decoder) throws {
        let response = try RawResponse(from: decoder)
        size = response.health.size
        title = response.health.name
    }
}

Использование:

let jsonData = """
{
    "health": {
        "size":{
            "width":150,
            "height":150
        },
        "name":"Apple"
    }
}
""".data(using: .utf8)!

do {
    let health = try JSONDecoder().decode(Health.self, from: jsonData)
    print(health.size)  // Size(width: 150, height: 150)
    print(health.title)  // Apple
} catch {
    print(error.localizedDescription)
}

Редактировать: если вам нужна также функция кодирования

Если вам также необходимо реализовать протокол Encodable, чтобы вы могли кодировать свои данные, как фактический ответ, то вы можете сделать это с помощью:

extension Health: Encodable {
    func encode(to encoder: Encoder) throws {
        let health = Health.RawResponse.PrivateHealth(size: size, name: title)
        let response = RawResponse(health: health)
        var container = encoder.singleValueContainer()
        try container.encode(response)
    }
}

Чтобы это работало, вам понадобитсячтобы ваша структура RawResponse, PrivateHealth и Size была также кодируемой

  • Изменить на private struct RawResponse: Encodable, Decodable {
  • Изменить наstruct PrivateHealth: Encodable, Decodable {
  • Изменить на struct Size: Encodable, Decodable {

Пример кодирования:

let health = Health(size: Health.Size(width: 150, height: 150), title: "Apple")
do {
    let encodedHealth = try JSONEncoder().encode(health)  // Encoded data
    // For checking purpose, you convert the data to string and print
    let jsonString = String(data: encodedHealth, encoding: .utf8)!
    print(jsonString)  // This will ensure data is encoded as your desired format
} catch {
    print(error.localizedDescription)
}
0 голосов
/ 25 мая 2018

Для этого вам нужно самостоятельно реализовать протокол Codable.Это не так уж сложно:

Попробуйте следующее на игровой площадке.

import Foundation

struct HealthType: Codable {
    let title: String
    let width: Double
    let height: Double

    enum CodingKeys: String, CodingKey
    {
        case health = "health"
        case title = "name"
        case width = "width"
        case height = "height"
        case size = "size"
    }
}

extension HealthType {

    init(from decoder: Decoder) throws {
        let healthTypeContainer = try decoder.container(keyedBy: CodingKeys.self)
        let health = try healthTypeContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .health)
        let size = try health.nestedContainer(keyedBy: CodingKeys.self, forKey: .size)
        let title = try health.decode(String.self, forKey: .title)
        let width = try size.decode(Double.self, forKey: .width)
        let height = try size.decode(Double.self, forKey: .height)
        self.init(title: title, width: width, height: height)
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        var health = container.nestedContainer(keyedBy: CodingKeys.self, forKey: .health)
        var size = health.nestedContainer(keyedBy: CodingKeys.self, forKey: .size)
        try health.encode(title, forKey: .title)
        try size.encode(width, forKey: .width)
        try size.encode(height, forKey: .height)
    }
}

let jsonData = """
{
"health": {
    "size":{
        "width":150,
        "height":150
    },
    "name":"Apple"
}
}
""".data(using: .utf8)!
do {
    print(jsonData)
    let healthType = try JSONDecoder().decode(HealthType.self, from: jsonData)
    print(healthType.title)  // Apple
    print(healthType.width)  // 150.0
    print(healthType.width)  // 150.0
} catch {
    print(error)
}
0 голосов
/ 24 мая 2018

Вы можете почти сделать эту работу.Тем не менее, JSONDecoder работает на Data вместо String, игровая площадка по умолчанию кодируется с использованием UTF-8, поэтому будет запускаться следующая игровая площадка:

import Cocoa

let jsonData = """
    {
        "health": {
            "size":{
                "width":150,
                "height":150
           },
           "name":"Apple"
        }
    }
    """.data(using: .utf8)!

struct HealthType: Codable {
    var health: Health
}

struct Health: Codable {
    var title: String
    var size: Size

    enum CodingKeys: String, CodingKey
    {
        case title = "name"
        case size
    }
}

struct Size: Codable {
    var width: Double
    var height: Double
}

do {
    let health = try JSONDecoder().decode(HealthType.self, from:jsonData)
    print(health)
    let h = health.health
    print(h.title)

} catch {
    print(error)
}

Пока это анализируется и работает хорошо, я не могусмысл вашего заявления "без создания Структурной модели для здоровья".Часть компромисса использования Codable заключается в том, что вам придется предоставить определения структуры соответствующей части вашего JSON.Вы также можете проанализировать ваш ввод в [String:Any].self, но работать с ним - перетаскивание.Вам придется постоянно оценивать касты и опциональные персонажи.При использовании протокола Codable для анализа ваши ошибки будут сконцентрированы на том, что decode может throw на вас.Информация, которую вы получаете, достаточно хороша для описания ошибок в вашем JSON (или в вашем struct в зависимости от вашей точки зрения).

Короче говоря, если ваш JSON содержит ваш "health" ключ, вам нужно будет сказать, что с ним делать.

...