web-dev-qa-db-ja.com

Swiftでエンコードされた値を含むEncodableを使用して構造体をエンコードする方法

すでにエンコードされたJSONフラグメントであるcontentsの値を含む、次のようなデータ構造を想像してください。

let partial = """
{ "foo": "Foo", "bar": 1 }
"""

struct Document {
  let contents: String
  let other: [String: Int]
}

let doc = Document(contents: partial, other: ["foo": 1])

望ましい出力

結合されたデータ構造は、contentsをそのまま使用し、otherをエンコードする必要があります。

{
  "contents": { "foo": "Foo", "bar": 1 },
  "other": { "foo": 1 }
}

Encodableの使用

次のEncodableの実装はDocumentをJSONとしてエンコードしますが、contentsも文字列に再エンコードします。つまり、引用符で囲まれ、すべての"引用符は\"にエスケープされました。

extension Document : Encodable {
    enum CodingKeys : String, CodingKey {
        case contents
        case other
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)

        try container.encode(contents, forKey: .contents)
        try container.encode(other, forKey: .other)
    }
}

出力

{
  "contents": "{\"foo\": \"Foo\", \"bar\": 1}",
  "other": { "foo": 1 }
}

encodeをそのままcontentsを通過させるにはどうすればよいですか?

5
klotz

あなたはこれを行うことでそれを達成することができます:

let partial = """
{
"foo": "Foo",
"bar": 1
}
"""

// declare a new type for `content` to deal with it as an object instead of a string
struct Document {
    let contents: Contents
    let other: [String: Int]

    struct Contents: Codable {
        let foo: String
        let bar: Int
    }
}

extension Document : Encodable {
    enum CodingKeys: String, CodingKey {
        case contents
        case other
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)

        try container.encode(contents, forKey: .contents)
        try container.encode(other, forKey: .other)
    }
}

let decoder = JSONDecoder()
let contents = try decoder.decode(Document.Contents.self, from: partial.data(using: .utf8)!)

let encoder = JSONEncoder()
let doc = Document(contents: contents, other: ["foo": 1])
let result = try encoder.encode(doc)
print(String(data: result, encoding: .utf8)!)

基本的に、最初にpartialをデコードして処理し、次にそのデコード結果をDocumentに渡すことができます。

出力は次のようになります。

{"other":{"foo":1}、 "contents":{"foo": "Foo"、 "bar":1}}

0
Ahmad F