152

Swift 4 では、新しいCodableプロトコルが追加されました。私が使用JSONDecoderすると、クラスのオプションではないすべてのプロパティがCodableJSON にキーを持つ必要があるように見えます。そうしないと、エラーがスローされます。

クラスのすべてのプロパティをオプションにすることは、本当に必要なのはjsonの値またはデフォルト値を使用することであるため、不必要な面倒のように思えます。(プロパティをnilにしたくありません。)

これを行う方法はありますか?

class MyCodable: Codable {
    var name: String = "Default Appleseed"
}

func load(input: String) {
    do {
        if let data = input.data(using: .utf8) {
            let result = try JSONDecoder().decode(MyCodable.self, from: data)
            print("name: \(result.name)")
        }
    } catch  {
        print("error: \(error)")
        // `Error message: "Key not found when expecting non-optional type
        // String for coding key \"name\""`
    }
}

let goodInput = "{\"name\": \"Jonny Appleseed\" }"
let badInput = "{}"
load(input: goodInput) // works, `name` is Jonny Applessed
load(input: badInput) // breaks, `name` required since property is non-optional
4

7 に答える 7

10

まったく同じものを探してこの質問に出くわしました。ここでの解決策が唯一の選択肢になるのではないかと心配していましたが、私が見つけた答えはあまり満足のいくものではありませんでした.

私の場合、カスタム デコーダーを作成するには大量のボイラープレートが必要になり、維持が困難になるため、他の答えを探し続けました。

を使用して単純なケースでこれを克服する興味深い方法を示すこの記事@propertyWrapperに出くわしました。私にとって最も重要なことは、再利用可能で、既存のコードのリファクタリングが最小限で済むことでした。

この記事では、欠落しているブール値のプロパティを失敗せずにデフォルトで false にしたい場合を想定していますが、他のさまざまなバリアントも示しています。詳細を読むことができますが、私のユースケースで何をしたかを示します。

私の場合、arrayキーが見つからない場合は空として初期化する必要がありました。

そこで、次の@propertyWrapper拡張機能と追加の拡張機能を宣言しました。

@propertyWrapper
struct DefaultEmptyArray<T:Codable> {
    var wrappedValue: [T] = []
}

//codable extension to encode/decode the wrapped value
extension DefaultEmptyArray: Codable {
    
    func encode(to encoder: Encoder) throws {
        try wrappedValue.encode(to: encoder)
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        wrappedValue = try container.decode([T].self)
    }
    
}

extension KeyedDecodingContainer {
    func decode<T:Decodable>(_ type: DefaultEmptyArray<T>.Type,
                forKey key: Key) throws -> DefaultEmptyArray<T> {
        try decodeIfPresent(type, forKey: key) ?? .init()
    }
}

この方法の利点は@propertyWrapper、プロパティに を追加するだけで、既存のコードの問題を簡単に解決できることです。私の場合:

@DefaultEmptyArray var items: [String] = []

これが同じ問題を扱っている人に役立つことを願っています。


アップデート:

問題を調査し続けながらこの回答を投稿した後、この他の記事@propertyWrapperを見つけましたが、最も重要なのは、これらの種類のケースで一般的な使いやすい s を含むそれぞれのライブラリです。

https://github.com/marksands/BetterCodable

于 2020-07-26T17:51:55.477 に答える