[json] Swift 4의 Codable에서 속성을 제외하는 방법

Swift 4의 새로운 Encodable/ Decodable프로토콜은 JSON (비) 직렬화를 매우 즐겁게 만듭니다. 그러나 어떤 속성을 인코딩하고 어떤 속성을 디코딩해야하는지 세밀하게 제어 할 수있는 방법을 아직 찾지 못했습니다.

수반되는 CodingKeys열거 형에서 속성을 제외하면 프로세스에서 속성이 모두 제외 된다는 것을 알았습니다 .하지만 더 세밀하게 제어 할 수있는 방법이 있습니까?



답변

인코딩 / 디코딩 할 키 목록은라는 유형에 의해 제어됩니다 CodingKeys.s 끝에 ). 컴파일러는이를 합성 할 수 있지만 항상 재정의 할 수 있습니다.

nickname인코딩 디코딩 모두 에서 속성을 제외하고 싶다고 가정 해 보겠습니다 .

struct Person: Codable {
    var firstName: String
    var lastName: String
    var nickname: String?

    private enum CodingKeys: String, CodingKey {
        case firstName, lastName
    }
}

비대칭이되도록하려면 (예 : 인코딩하지만 디코딩하지 않거나 그 반대의 경우) encode(with encoder: )및 의 자체 구현을 제공해야합니다 init(from decoder: ).

struct Person: Codable {
    var firstName: String
    var lastName: String

    // Since fullName is a computed property, it's excluded by default
    var fullName: String {
        return firstName + " " + lastName
    }

    private enum CodingKeys: String, CodingKey {
        case firstName
        case lastName
        case fullName
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        firstName = try container.decode(String.self, forKey: .firstName)
        lastName = try container.decode(String.self, forKey: .lastName)
    }

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


답변

구조의 많은 속성 집합에서 몇 가지 속성의 디코딩을 제외해야하는 경우 해당 속성을 선택적 속성으로 선언합니다. 선택 사항을 풀기위한 코드는 CodingKey 열거 형 아래에 많은 키를 작성하는 것보다 적습니다.

확장을 사용하여 계산 된 인스턴스 속성과 계산 된 유형 속성을 추가하는 것이 좋습니다. 코딩 가능한 컴 포밍 속성을 다른 로직과 분리하여 더 나은 가독성을 제공합니다.


답변

인코더에서 일부 속성을 제외하는 또 다른 방법은 별도의 코딩 컨테이너를 사용할 수 있습니다.

struct Person: Codable {
    let firstName: String
    let lastName: String
    let excludedFromEncoder: String

    private enum CodingKeys: String, CodingKey {
        case firstName
        case lastName
    }
    private enum AdditionalCodingKeys: String, CodingKey {
        case excludedFromEncoder
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let anotherContainer = try decoder.container(keyedBy: AdditionalCodingKeys.self)
        firstName = try container.decode(String.self, forKey: .firstName)
        lastName = try container.decode(String.self, forKey: .lastName)

        excludedFromEncoder = try anotherContainer(String.self, forKey: . excludedFromEncoder)
    }

    // it is not necessary to implement custom encoding
    // func encode(to encoder: Encoder) throws

    // let person = Person(firstName: "fname", lastName: "lname", excludedFromEncoder: "only for decoding")
    // let jsonData = try JSONEncoder().encode(person)
    // let jsonString = String(data: jsonData, encoding: .utf8)
    // jsonString --> {"firstName": "fname", "lastName": "lname"}

}

디코더에도 동일한 접근 방식을 사용할 수 있습니다.


답변

계산 된 속성을 사용할 수 있습니다.

struct Person: Codable {
  var firstName: String
  var lastName: String
  var nickname: String?

  var nick: String {
    get {
      nickname ?? ""
    }
  }

  private enum CodingKeys: String, CodingKey {
    case firstName, lastName
  }
}


답변

이 작업을 수행 수 있지만 궁극적으로 매우 불안정 하고 심지어 비정규 적 입니다. 나는 당신이 어디에서 왔는지 알고 있다고 생각합니다. #ids 의 개념은 HTML에서 널리 퍼져 있지만 JSON내가 생각하는 세계로 거의 전달되지 않습니다 . 좋은 것으로 (TM) .

일부 Codable구조체는 JSON재귀 해시를 사용 하여 파일을 재구성하면 파일을 잘 구문 분석 할 수 있습니다 . 즉 recipe, 배열에 ingredients(하나 또는 여러)를 차례로 포함하는 경우입니다 ingredient_info. 이렇게하면 파서가 처음에 네트워크를 연결하는 데 도움이되며 실제로 필요한 경우 구조를 간단한 순회를 통해 일부 백 링크 만 제공 하면됩니다 . 이를 위해서는 귀하 JSON 귀하의 데이터 구조에 대한 철저한 재 작업이 필요하기 때문에 귀하가 그것에 대해 생각할 수 있도록 아이디어를 스케치 할뿐입니다. 수용 가능하다고 생각되는 경우 의견에 저에게 알려 주시면 더 자세히 설명해 드릴 수 있지만 상황에 따라 둘 중 하나를 변경할 자유가 없을 수도 있습니다.


답변

저는 AssociatedObject와 함께 프로토콜 및 확장을 사용하여 이미지 (또는 Codable에서 제외해야하는 모든 속성) 속성을 설정하고 가져 왔습니다.

이것으로 우리는 우리 자신의 인코더와 디코더를 구현할 필요가 없습니다.

다음은 단순성을 위해 관련 코드를 유지하는 코드입니다.

protocol SCAttachmentModelProtocol{
    var image:UIImage? {get set}
    var anotherProperty:Int {get set}
}
extension SCAttachmentModelProtocol where Self: SCAttachmentUploadRequestModel{
    var image:UIImage? {
        set{
            //Use associated object property to set it
        }
        get{
            //Use associated object property to get it
        }
    }
}
class SCAttachmentUploadRequestModel : SCAttachmentModelProtocol, Codable{
    var anotherProperty:Int
}

이제 Image 속성에 액세스하려고 할 때마다 프로토콜 (SCAttachmentModelProtocol)을 확인하는 객체에서 사용할 수 있습니다.


답변