JSON с описанием ключа - PullRequest
       26

JSON с описанием ключа

1 голос
/ 27 марта 2020

Я получаю данные с моего Gitlab-сервера с помощью Alamofire и, похоже, не могу правильно установить CodingKey.

Вот две модели, которые я использую для этого (сокращенно):

struct Issue: Codable {
    let id: Int
    var iid: Int? = nil
    var project_id: Int? = nil
    var state: String?
    var title: String? = nil
    var description: String? = nil
    var labels: [Label]? = []  // custom type, see below
}
struct Label {
    let id: Int
    var name: String
    var color: String = "#FFAABB"
    var labelDescription: String = ""
}

extension Label: CustomStringConvertible {
    var description: String {
        return "Label: \(name)"
    }
}

extension Label: Codable {
    enum CodingKeys: String, CodingKey {
        case id
        case name
        case color
        case labelDescription = "description"
    }
}

extension Label: Equatable {
    static func ==(lhs: Label, rhs: Label) -> Bool {
        return lhs.id == rhs.id
    }
}

Теперь, при получении моих проблем с Alamofire, я вижу, что JSON возвращается, например,

{
  "id": 402,
  "iid": 78,
  "project_id": 34,
  "title": "Feature: Prioritization / Order of issues in table view",
  "description": "On the web, it is possible to have the issues ordered differently (e.g. by date added or manually).\n\nCheck, how  `manually` is implemented and whether this ordering information is coming with the api.\nIf it is, could this be used to do reorders in the tableview?",
  "state": "opened",
  "created_at": "2020-03-26T14:08:21.355Z",
  "updated_at": "2020-03-27T12:58:03.418Z",
  "closed_at": null,
  "closed_by": null,
  "labels": [{
    "id": 81,
    "name": "P1",
    "color": "#428BCA",
    "description": "Priority 1 of 4",
    "description_html": "Priority 1 of 4",
    "text_color": "#FFFFFF"
  }],
...
}

Кодирование завершается неудачно, хотя Alamofire показывает следующую ошибку:

[Request]: GET https: //dev.appfros.ch/api/v4/projects/34/labels
[Request Body]: 
None
[Response]: 
[Status Code]: 200
[Headers]:
Cache-Control: max-age=0, private, must-revalidate
Content-Length: 929
Content-Type: application/json
Date: Fri, 27 Mar 2020 14:08:59 GMT
Etag: W/\"758e92848542f37113453292b85cbe17\"
Link: <https://dev.appfros.ch/api/v4/projects/34/labels?id=34&include_ancestor_groups=true&page=1&per_page=20&with_counts=false>; rel=\"first\", <https://dev.appfros.ch/api/v4/projects/34/labels?id=34&include_ancestor_groups=true&page=1&per_page=20&with_counts=false>; rel=\"last\"
Server: nginx/1.17.6
Strict-Transport-Security: max-age=31536000, max-age=31536000
Vary: Origin
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
x-next-page: 
x-page: 1
x-per-page: 20
x-prev-page: 
x-request-id: BdvjZaS8hl4
x-runtime: 0.052360
x-total: 5
x-total-pages: 1
[Response Body]: 
[{\"id\":81,\"name\":\"P1\",\"color\":\"#428BCA\",\"description\":\"Priority 1 of 4\",\"description_html\":\"Priority 1 of 4\",\"text_color\":\"#FFFFFF\",\"subscribed\":false,\"priority\":null,\"is_project_label\":true},{\"id\":82,\"name\":\"P2\",\"color\":\"#428BCA\",\"description\":\"Priority 2 of 4\",\"description_html\":\"Priority 2 of 4\",\"text_color\":\"#FFFFFF\",\"subscribed\":false,\"priority\":null,\"is_project_label\":true},{\"id\":83,\"name\":\"P3\",\"color\":\"#428BCA\",\"description\":\"Priority 3 of 4\",\"description_html\":\"Priority 3 of 4\",\"text_color\":\"#FFFFFF\",\"subscribed\":false,\"priority\":null,\"is_project_label\":true},{\"id\":84,\"name\":\"P4\",\"color\":\"#428BCA\",\"description\":\"Priority 4 of 4\",\"description_html\":\"Priority 4 of 4\",\"text_color\":\"#FFFFFF\",\"subscribed\":false,\"priority\":null,\"is_project_label\":true},{\"id\":79,\"name\":\"WIP\",\"color\":\"#004E00\",\"description\":null,\"description_html\":\"\",\"text_color\":\"#FFFFFF\",\"subscribed\":false,\"priority\":null,\"is_project_label\":true}]
[Data]: 929 bytes
[Network Duration]: 0.3039969205856323s
[Serialization Duration]: 0.003159046173095703s
[Result]: failure(Alamofire.AFError.responseSerializationFailed(reason: Alamofire.AFError.ResponseSerializationFailureReason.decodingFailed(error: Swift.DecodingError.valueNotFound(Swift.String, Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: \"Index 4\", intValue: 4), CodingKeys(stringValue: \"description\", intValue: nil)], debugDescription: \"Expected String value but found null instead.\", underlyingError: nil)))))

Кроме того, тестирование показывает, что когда я опускаю клавишу description типа Label, все работает. Очевидно, я хочу преобразовать ключ description в labelDescription в моей модели, потому что description уже необходим для соответствия CustomStringConvertible - но даже когда я удаляю это соответствие и изменяю свою модель Labels на следующую, Я получаю ошибку ...

struct Label {
    let id: Int
    var name: String
    var color: String = "#FFAABB"
    var description: String = ""
}

1 Ответ

1 голос
/ 28 марта 2020

Вы можете переопределить init(from decoder:) и decodeIfPresent(_,forKey:)

Из документации:

Этот метод возвращает nil, если у контейнера нет значение, связанное с ключом, или, если это значение null. Разницу между этими состояниями можно различить с помощью вызова contains(_:).

В вашем случае:

extension Label {
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decode(Int.self, forKey: .id)
        name = try container.decode(String.self, forKey: .name)
        color = try container.decode(String.self, forKey: .color)
        labelDescription = try container.decodeIfPresent(String.self, forKey: .labelDescription) ?? ""
    }
}
...