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


107

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

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


인코딩하려는 속성이 있지만 디코딩하려는 속성이 다른 경우가 있다는 말입니까? (즉, 유형을 왕복 할 수 없도록 하시겠습니까?) 속성을 제외하는 데 관심이 있다면 기본값을 제공하고 CodingKeys열거 형에서 제외하는 것으로 충분하기 때문입니다.
Itai Ferber

어쨌든 프로세스를 완전히 제어하기 위해 Codable프로토콜 의 요구 사항 ( init(from:)encode(to:))을 수동으로 구현할 수 있습니다 .
Itai Ferber

내 특정 사용 사례는 디코더에 너무 많은 제어 권한을 부여하지 않는 것입니다. 이로 인해 내부 속성 값을 덮어 쓰면서 원격으로 JSON을 얻을 수 있습니다. 아래 솔루션이 적합합니다!
RamwiseMatt

1
일반적으로 무료로 받아야하는 모든 속성을 다시 구현하는 대신 특수한 경우와 제외 된 키만 처리하면되는 답변 / 새로운 Swift 기능을보고 싶습니다.
pkamb

답변:


187

인코딩 / 디코딩 할 키 목록은라는 유형에 의해 제어됩니다 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)
    }
}

18
nickname이것이 작동하려면 기본값 을 제공해야합니다 . 그렇지 않으면의 속성에 할당 할 수있는 값이 없습니다 init(from:).
Itai Ferber

1
@ItaiFerber 원래 내 Xcode에 있던 옵션으로 전환했습니다
Code Different

encode비대칭 예제에서 를 제공해야 합니까? 그것이 여전히 표준 행동이기 때문에 나는 그것이 필요하다고 생각하지 않았습니다. 그냥 decode비대칭에서 오는 그의입니다.
Mark A. Donohoe

1
@MarqueIV 예,해야합니다. 이후 fullName저장된 속성에 매핑 할 수없는 사용자 지정 인코더 및 디코더를 제공해야합니다.
Code Different

2

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

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


2

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

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"}

}

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


1

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

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
  }
}

이것이 저에게 단서였습니다. lazy var효과적으로 런타임 속성을 사용하여 Codable에서 제외했습니다.
ChrisH

0

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

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


0

저는 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)을 확인하는 객체에서 사용할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.