Swift4のCodableからプロパティを除外する方法


104

Swift 4の新しいEncodable/Decodableプロトコルは、JSON(逆)シリアル化を非常に快適にします。ただし、どのプロパティをエンコードし、どのプロパティをデコードするかをきめ細かく制御する方法はまだ見つかりません。

付随するCodingKeys列挙型からプロパティを除外すると、プロセスからプロパティが完全に除外されることに気付きましたが、よりきめ細かい制御を行う方法はありますか?


エンコードしたいプロパティがいくつかあるが、デコードしたいプロパティが異なる場合があると言っていますか?(つまり、タイプをラウンドトリップできないようにしたいですか?)プロパティを除外するだけの場合は、デフォルト値を指定してCodingKeys列挙型から除外するだけで十分です。
Itai Ferber 2017年

とにかく、プロセスを完全に制御するために、Codableプロトコル(init(from:)およびencode(to:))の要件をいつでも手動で実装できます。
Itai Ferber 2017年

私の特定のユースケースは、デコーダーに過度の制御を与えないようにすることです。これにより、内部プロパティ値の上書きからリモートで取得されたJSONが発生する可能性があります。以下の解決策で十分です!
RamwiseMatt 2017年

1
通常は無料で入手できるすべてのプロパティを再実装するのではなく、特殊なケースと除外されたキーの処理のみを必要とする回答/新しいSwift機能を確認したいと思います。
pkamb 2018

回答:


182

エンコード/デコードするキーのリストは、と呼ばれるタイプによって制御されますCodingKeyss最後のに)。コンパイラーはこれを合成できますが、いつでもオーバーライドできます。

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

17
nicknameこれを機能させるには、デフォルト値を指定する必要があります。それ以外の場合、のプロパティに割り当てることができる値はありませんinit(from:)
Itai Ferber

1
@ItaiFerberオプションに切り替えました。これは元々Xcodeに含まれていました
コードが異なり

encode非対称の例でを提供する必要がありますか?それはまだ標準的な動作なので、必要だとは思いませんでした。それdecode以来、非対称性が生じています。
マークA.ドノホ2018年

1
@MarqueIVはい、そうする必要があります。以来fullName保存されたプロパティにマップすることができない、カスタムエンコーダとデコーダを提供する必要があります。
コードが異なる

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

これは可能ですが、最終的には非常に迅速でなく、JSONyでさえありません。あなたがどこから来たのかはわかります。sの概念は#idHTMLで普及していますが、それが良いことだJSONと思う世界に持ち込まれることはめったにありません。(TM)。

一部のCodable構造体はJSON、再帰ハッシュを使用してファイルを再構築すると、ファイルを正常に解析できます。つまりrecipe、配列にingredients(1つまたは複数の)が含まれているだけの場合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.