Cara mendekode properti dengan tipe kamus JSON dalam protokol dekode Swift 4

103

Katakanlah saya memiliki Customertipe data yang berisi metadataproperti yang dapat berisi kamus JSON apa pun di objek pelanggan

struct Customer {
  let id: String
  let email: String
  let metadata: [String: Any]
}

{  
  "object": "customer",
  "id": "4yq6txdpfadhbaqnwp3",
  "email": "[email protected]",
  "metadata": {
    "link_id": "linked-id",
    "buy_count": 4
  }
}

The metadataproperti bisa sewenang-wenang objek peta JSON.

Sebelum saya dapat mentransmisikan properti dari JSON deserialized dari NSJSONDeserializationtetapi dengan Swift 4 baruDecodable protokol yang baru, saya masih tidak dapat memikirkan cara untuk melakukannya.

Adakah yang tahu bagaimana mencapai ini di Swift 4 dengan protokol Decodable?

Pitiphong Phongpattranont
sumber

Jawaban:

89

Dengan beberapa inspirasi dari inti yang saya temukan, saya menulis beberapa ekstensi untuk UnkeyedDecodingContainerdan KeyedDecodingContainer. Anda dapat menemukan tautan ke inti saya di sini . Dengan menggunakan kode ini Anda sekarang dapat memecahkan kode apa pun Array<Any>atau Dictionary<String, Any>dengan sintaks yang sudah dikenal:

let dictionary: [String: Any] = try container.decode([String: Any].self, forKey: key)

atau

let array: [Any] = try container.decode([Any].self, forKey: key)

Sunting: ada satu peringatan yang saya temukan yaitu mendekodekan array kamus [[String: Any]]. Sintaks yang diperlukan adalah sebagai berikut. Anda mungkin ingin membuat kesalahan alih-alih memaksa casting:

let items: [[String: Any]] = try container.decode(Array<Any>.self, forKey: .items) as! [[String: Any]]

EDIT 2: Jika Anda hanya ingin mengonversi seluruh file ke kamus, lebih baik Anda tetap menggunakan api dari JSONSerialization karena saya belum menemukan cara untuk memperluas JSONDecoder itu sendiri untuk langsung memecahkan kode kamus.

guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
  // appropriate error handling
  return
}

Ekstensi

// Inspired by https://gist.github.com/mbuchetics/c9bc6c22033014aa0c550d3b4324411a

struct JSONCodingKeys: CodingKey {
    var stringValue: String

    init?(stringValue: String) {
        self.stringValue = stringValue
    }

    var intValue: Int?

    init?(intValue: Int) {
        self.init(stringValue: "\(intValue)")
        self.intValue = intValue
    }
}


extension KeyedDecodingContainer {

    func decode(_ type: Dictionary<String, Any>.Type, forKey key: K) throws -> Dictionary<String, Any> {
        let container = try self.nestedContainer(keyedBy: JSONCodingKeys.self, forKey: key)
        return try container.decode(type)
    }

    func decodeIfPresent(_ type: Dictionary<String, Any>.Type, forKey key: K) throws -> Dictionary<String, Any>? {
        guard contains(key) else { 
            return nil
        }
        guard try decodeNil(forKey: key) == false else { 
            return nil 
        }
        return try decode(type, forKey: key)
    }

    func decode(_ type: Array<Any>.Type, forKey key: K) throws -> Array<Any> {
        var container = try self.nestedUnkeyedContainer(forKey: key)
        return try container.decode(type)
    }

    func decodeIfPresent(_ type: Array<Any>.Type, forKey key: K) throws -> Array<Any>? {
        guard contains(key) else {
            return nil
        }
        guard try decodeNil(forKey: key) == false else { 
            return nil 
        }
        return try decode(type, forKey: key)
    }

    func decode(_ type: Dictionary<String, Any>.Type) throws -> Dictionary<String, Any> {
        var dictionary = Dictionary<String, Any>()

        for key in allKeys {
            if let boolValue = try? decode(Bool.self, forKey: key) {
                dictionary[key.stringValue] = boolValue
            } else if let stringValue = try? decode(String.self, forKey: key) {
                dictionary[key.stringValue] = stringValue
            } else if let intValue = try? decode(Int.self, forKey: key) {
                dictionary[key.stringValue] = intValue
            } else if let doubleValue = try? decode(Double.self, forKey: key) {
                dictionary[key.stringValue] = doubleValue
            } else if let nestedDictionary = try? decode(Dictionary<String, Any>.self, forKey: key) {
                dictionary[key.stringValue] = nestedDictionary
            } else if let nestedArray = try? decode(Array<Any>.self, forKey: key) {
                dictionary[key.stringValue] = nestedArray
            }
        }
        return dictionary
    }
}

extension UnkeyedDecodingContainer {

    mutating func decode(_ type: Array<Any>.Type) throws -> Array<Any> {
        var array: [Any] = []
        while isAtEnd == false {
            // See if the current value in the JSON array is `null` first and prevent infite recursion with nested arrays.
            if try decodeNil() {
                continue
            } else if let value = try? decode(Bool.self) {
                array.append(value)
            } else if let value = try? decode(Double.self) {
                array.append(value)
            } else if let value = try? decode(String.self) {
                array.append(value)
            } else if let nestedDictionary = try? decode(Dictionary<String, Any>.self) {
                array.append(nestedDictionary)
            } else if let nestedArray = try? decode(Array<Any>.self) {
                array.append(nestedArray)
            }
        }
        return array
    }

    mutating func decode(_ type: Dictionary<String, Any>.Type) throws -> Dictionary<String, Any> {

        let nestedContainer = try self.nestedContainer(keyedBy: JSONCodingKeys.self)
        return try nestedContainer.decode(type)
    }
}
pembual
sumber
Menarik, saya akan mencoba inti ini dan akan memperbarui hasilnya kepada Anda @loudmouth
Pitiphong Phongpattranont
@PitiphongPhongpattranont apakah kode ini berhasil untuk Anda?
pengeras suara
1
@JonBrooks kondisi terakhir di dalam UnkeyedDecodingContainer's decode(_ type: Array<Any>.Type) throws -> Array<Any>adalah memeriksa untuk bersarang array yang. Jadi jika Anda memiliki struktur data yang terlihat seperti berikut: [true, 452.0, ["a", "b", "c"] ] Ini akan menarik ["a", "b", "c"]array bersarang . The decodeMetode dari UnkeyedDecodingContainer"muncul" off elemen dari wadah. Itu seharusnya tidak menyebabkan rekursi tak terbatas.
pengeras suara
1
@loudmouth adalah mungkin untuk memiliki nilai nihil untuk kunci di json: {"array": null}. Jadi Anda guard contains(key)akan lulus tetapi akan crash beberapa baris kemudian ketika mencoba untuk memecahkan kode nilai null untuk kunci "array". Jadi lebih baik menambahkan satu kondisi lagi untuk memeriksa apakah nilainya sebenarnya bukan null sebelum memanggil decode.
chebur
2
Saya menemukan perbaikan: Alih-alih } else if let nestedArray = try? decode(Array<Any>.self, forKey: key)mencoba:} else if var nestedContainer = try? nestedUnkeyedContainer(), let nestedArray = try? nestedContainer.decode(Array<Any>.self) {
Jon Brooks
23

Saya telah bermain dengan masalah ini juga, dan akhirnya menulis perpustakaan sederhana untuk bekerja dengan tipe "JSON generik" . (Di mana "generik" berarti "tanpa struktur yang diketahui sebelumnya".) Poin utama mewakili JSON generik dengan tipe konkret:

public enum JSON {
    case string(String)
    case number(Float)
    case object([String:JSON])
    case array([JSON])
    case bool(Bool)
    case null
}

Jenis ini kemudian dapat mengimplementasikan Codabledan Equatable.

zoul
sumber
13

Anda dapat membuat struct metadata yang mengonfirmasi Decodableprotokol dan menggunakan JSONDecoderkelas untuk membuat objek dari data dengan menggunakan metode decode seperti di bawah ini

let json: [String: Any] = [
    "object": "customer",
    "id": "4yq6txdpfadhbaqnwp3",
    "email": "[email protected]",
    "metadata": [
        "link_id": "linked-id",
        "buy_count": 4
    ]
]

struct Customer: Decodable {
    let object: String
    let id: String
    let email: String
    let metadata: Metadata
}

struct Metadata: Decodable {
    let link_id: String
    let buy_count: Int
}

let data = try JSONSerialization.data(withJSONObject: json, options: .prettyPrinted)

let decoder = JSONDecoder()
do {
    let customer = try decoder.decode(Customer.self, from: data)
    print(customer)
} catch {
    print(error.localizedDescription)
}
Suhit Patil
sumber
10
Tidak, saya tidak bisa, karena saya tidak tahu struktur metadatanilainya. Itu bisa berupa objek sewenang-wenang.
Pitiphong Phongpattranont
Apakah maksud Anda itu bisa berupa tipe Array atau Dictionary?
Suhit Patil
dapat Anda memberikan contoh atau menambahkan penjelasan lebih lanjut tentang struktur metadata
Suhit Patil
2
Nilai metadatadapat berupa objek JSON apa pun. Jadi bisa kamus kosong atau kamus apapun. "metadata": {} "metadata": {user_id: "id"} "metadata": {preferensi: {shows_value: true, language: "en"}} dll.
Pitiphong Phongpattranont
satu opsi yang mungkin adalah menggunakan semua parameter dalam struct metadata sebagai opsional dan mencantumkan semua kemungkinan nilai dalam metadata struct seperti struct metadata {var user_id: String? preferensi var: String? }
Suhit Patil
8

Saya datang dengan solusi yang sedikit berbeda.

Misalkan kita memiliki sesuatu yang lebih dari yang sederhana [String: Any] untuk diurai, jika Any mungkin berupa larik atau kamus bersarang atau kamus larik.

Sesuatu seperti ini:

var json = """
{
  "id": 12345,
  "name": "Giuseppe",
  "last_name": "Lanza",
  "age": 31,
  "happy": true,
  "rate": 1.5,
  "classes": ["maths", "phisics"],
  "dogs": [
    {
      "name": "Gala",
      "age": 1
    }, {
      "name": "Aria",
      "age": 3
    }
  ]
}
"""

Nah, inilah solusi saya:

public struct AnyDecodable: Decodable {
  public var value: Any

  private struct CodingKeys: CodingKey {
    var stringValue: String
    var intValue: Int?
    init?(intValue: Int) {
      self.stringValue = "\(intValue)"
      self.intValue = intValue
    }
    init?(stringValue: String) { self.stringValue = stringValue }
  }

  public init(from decoder: Decoder) throws {
    if let container = try? decoder.container(keyedBy: CodingKeys.self) {
      var result = [String: Any]()
      try container.allKeys.forEach { (key) throws in
        result[key.stringValue] = try container.decode(AnyDecodable.self, forKey: key).value
      }
      value = result
    } else if var container = try? decoder.unkeyedContainer() {
      var result = [Any]()
      while !container.isAtEnd {
        result.append(try container.decode(AnyDecodable.self).value)
      }
      value = result
    } else if let container = try? decoder.singleValueContainer() {
      if let intVal = try? container.decode(Int.self) {
        value = intVal
      } else if let doubleVal = try? container.decode(Double.self) {
        value = doubleVal
      } else if let boolVal = try? container.decode(Bool.self) {
        value = boolVal
      } else if let stringVal = try? container.decode(String.self) {
        value = stringVal
      } else {
        throw DecodingError.dataCorruptedError(in: container, debugDescription: "the container contains nothing serialisable")
      }
    } else {
      throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not serialise"))
    }
  }
}

Coba gunakan

let stud = try! JSONDecoder().decode(AnyDecodable.self, from: jsonData).value as! [String: Any]
print(stud)
Giuseppe Lanza
sumber
6

Ketika saya menemukan jawaban lama, saya hanya menguji kasus objek JSON sederhana tetapi bukan yang kosong yang akan menyebabkan pengecualian runtime seperti @slurmomatic dan @zoul ditemukan. Maaf untuk masalah ini.

Jadi saya mencoba cara lain dengan memiliki protokol JSONValue sederhana, mengimplementasikan AnyJSONValuetipe erasure struct dan menggunakan tipe itu sebagai gantinya Any. Berikut implementasinya.

public protocol JSONType: Decodable {
    var jsonValue: Any { get }
}

extension Int: JSONType {
    public var jsonValue: Any { return self }
}
extension String: JSONType {
    public var jsonValue: Any { return self }
}
extension Double: JSONType {
    public var jsonValue: Any { return self }
}
extension Bool: JSONType {
    public var jsonValue: Any { return self }
}

public struct AnyJSONType: JSONType {
    public let jsonValue: Any

    public init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()

        if let intValue = try? container.decode(Int.self) {
            jsonValue = intValue
        } else if let stringValue = try? container.decode(String.self) {
            jsonValue = stringValue
        } else if let boolValue = try? container.decode(Bool.self) {
            jsonValue = boolValue
        } else if let doubleValue = try? container.decode(Double.self) {
            jsonValue = doubleValue
        } else if let doubleValue = try? container.decode(Array<AnyJSONType>.self) {
            jsonValue = doubleValue
        } else if let doubleValue = try? container.decode(Dictionary<String, AnyJSONType>.self) {
            jsonValue = doubleValue
        } else {
            throw DecodingError.typeMismatch(JSONType.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Unsupported JSON tyep"))
        }
    }
}

Dan inilah cara menggunakannya saat mendekode

metadata = try container.decode ([String: AnyJSONValue].self, forKey: .metadata)

Masalah dengan masalah ini adalah kita harus menelepon value.jsonValue as? Int. Kita perlu menunggu sampai Conditional Conformancemendarat di Swift, itu akan menyelesaikan masalah ini atau setidaknya membantunya menjadi lebih baik.


[Jawaban Lama]

Saya memposting pertanyaan ini di forum Pengembang Apple dan ternyata itu sangat mudah.

dapat saya lakukan

metadata = try container.decode ([String: Any].self, forKey: .metadata)

di penginisialisasi.

Itu buruk bagiku untuk melewatkannya sejak awal.

Pitiphong Phongpattranont
sumber
4
Bisa memposting link pertanyaan di Apple Developer. Anytidak sesuai dengan Decodablejadi saya tidak yakin bagaimana ini adalah jawaban yang benar.
Reza Shirazian
@RezaShirazian Itulah yang saya pikirkan sejak awal. Tetapi ternyata Dictionary sesuai dengan Encodable ketika kuncinya sesuai dengan Hashable dan tidak bergantung pada nilainya. Anda dapat membuka header Kamus dan melihatnya sendiri. extension Dictionary: Encodable where Key: Hashable extension Dictionary: Decodable where Key: Hashable forums.developer.apple.com/thread/80288#237680
Pitiphong Phongpattranont
6
saat ini ini tidak berhasil. "Dictionary <String, Any> tidak sesuai dengan Decodable karena Any tidak sesuai dengan Decodable"
mbuchetics
Ternyata itu berhasil. Saya menggunakannya dalam kode saya. Anda perlu memahami bahwa tidak ada cara untuk menyatakan persyaratan bahwa "Nilai Kamus harus sesuai dengan protokol yang Dapat Didekodekan untuk membuat Kamus agar sesuai dengan protokol yang Dapat Didekodekan" sekarang. Itulah "Conditional Conformance" yang belum diimplementasikan di Swift 4 Saya rasa tidak apa-apa untuk saat ini karena ada banyak batasan di Swift Type System (dan Generik). Jadi ini berfungsi untuk saat ini tetapi ketika Sistem Tipe Swift membaik di masa depan (terutama ketika Kesesuaian Bersyarat diterapkan), ini seharusnya tidak berfungsi.
Pitiphong Phongpattranont
3
Tidak berfungsi untuk saya sejak Xcode 9 beta 5. Dikompilasi, tetapi meledak saat runtime: Dictionary <String, Any> tidak sesuai dengan Decodable karena Any tidak sesuai dengan Decodable.
zoul
6

Jika Anda menggunakan SwiftyJSON untuk mengurai JSON, Anda dapat memperbarui ke 4.1.0 yang memiliki Codabledukungan protokol. Cukup nyatakan metadata: JSONdan Anda sudah siap.

import SwiftyJSON

struct Customer {
  let id: String
  let email: String
  let metadata: JSON
}
allen huang
sumber
Saya tidak tahu mengapa jawaban ini tidak disukai. Ini benar-benar valid dan menyelesaikan masalah.
Leonid Usov
Tampaknya bagus untuk migrasi dari SwiftyJSON ke Decodable
Michał Ziobro
Ini tidak menyelesaikan bagaimana kemudian mengurai metadata json yang merupakan masalah asli.
llamacorn
1

Anda mungkin pernah melihat BeyovaJSON

import BeyovaJSON

struct Customer: Codable {
  let id: String
  let email: String
  let metadata: JToken
}

//create a customer instance

customer.metadata = ["link_id": "linked-id","buy_count": 4]

let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted 
print(String(bytes: try! encoder.encode(customer), encoding: .utf8)!)
canius
sumber
Ooh, sangat bagus. Menggunakannya untuk menerima JSON umum sebagai JToken, menambahkan beberapa nilai dan kembali ke server. Memang sangat bagus. Itu pekerjaan luar biasa yang telah Anda lakukan :)
Vitor Hugo Schwaab
1

Cara termudah dan disarankan adalah dengan membuat model terpisah untuk setiap kamus atau model yang ada di JSON .

Inilah yang saya lakukan

//Model for dictionary **Metadata**

struct Metadata: Codable {
    var link_id: String?
    var buy_count: Int?
}  

//Model for dictionary **Customer**

struct Customer: Codable {
   var object: String?
   var id: String?
   var email: String?
   var metadata: Metadata?
}

//Here is our decodable parser that decodes JSON into expected model

struct CustomerParser {
    var customer: Customer?
}

extension CustomerParser: Decodable {

//keys that matches exactly with JSON
enum CustomerKeys: String, CodingKey {
    case object = "object"
    case id = "id"
    case email = "email"
    case metadata = "metadata"
}

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CustomerKeys.self) // defining our (keyed) container

    let object: String = try container.decode(String.self, forKey: .object) // extracting the data
    let id: String = try container.decode(String.self, forKey: .id) // extracting the data
    let email: String = try container.decode(String.self, forKey: .email) // extracting the data

   //Here I have used metadata model instead of dictionary [String: Any]
    let metadata: Metadata = try container.decode(Metadata.self, forKey: .metadata) // extracting the data

    self.init(customer: Customer(object: object, id: id, email: email, metadata: metadata))

    }
}

Pemakaian:

  if let url = Bundle.main.url(forResource: "customer-json-file", withExtension: "json") {
        do {
            let jsonData: Data =  try Data(contentsOf: url)
            let parser: CustomerParser = try JSONDecoder().decode(CustomerParser.self, from: jsonData)
            print(parser.customer ?? "null")

        } catch {

        }
    }

** Saya telah menggunakan opsional untuk berada di sisi aman saat mengurai, dapat diubah sesuai kebutuhan.

Baca lebih lanjut tentang topik ini

minhazur.dll
sumber
1
Jawaban Anda adalah jawaban yang tepat untuk Swift 4.1 dan baris pertama posting Anda sudah mati! Dengan asumsi data berasal dari layanan web. Anda dapat memodelkan objek bersarang sederhana kemudian menggunakan sintaks titik untuk mengambil masing-masing. Lihat jawaban suhit di bawah ini.
David H
1

Saya telah membuat pod untuk memudahkan cara decoding + encoding [String: Any], [Any]. Dan ini menyediakan encode atau decode properti opsional, di sini https://github.com/levantAJ/AnyCodable

pod 'DynamicCodable', '1.0'

Bagaimana cara menggunakannya:

import DynamicCodable

struct YourObject: Codable {
    var dict: [String: Any]
    var array: [Any]
    var optionalDict: [String: Any]?
    var optionalArray: [Any]?

    enum CodingKeys: String, CodingKey {
        case dict
        case array
        case optionalDict
        case optionalArray
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        dict = try values.decode([String: Any].self, forKey: .dict)
        array = try values.decode([Any].self, forKey: .array)
        optionalDict = try values.decodeIfPresent([String: Any].self, forKey: .optionalDict)
        optionalArray = try values.decodeIfPresent([Any].self, forKey: .optionalArray)
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(dict, forKey: .dict)
        try container.encode(array, forKey: .array)
        try container.encodeIfPresent(optionalDict, forKey: .optionalDict)
        try container.encodeIfPresent(optionalArray, forKey: .optionalArray)
    }
}
Tai Le
sumber
0

Berikut ini lebih umum (tidak hanya [String: Any], tetapi[Any] pendekatan yang dapat diterjemahkan) dan dikemas (entitas terpisah digunakan untuk itu) terinspirasi oleh jawaban @loudmouth.

Menggunakannya akan terlihat seperti:

extension Customer: Decodable {
  public init(from decoder: Decoder) throws {
    let selfContainer = try decoder.container(keyedBy: CodingKeys.self)
    id = try selfContainer.decode(.id)
    email = try selfContainer.decode(.email)
    let metadataContainer: JsonContainer = try selfContainer.decode(.metadata)
    guard let metadata = metadataContainer.value as? [String: Any] else {
      let context = DecodingError.Context(codingPath: [CodingKeys.metadata], debugDescription: "Expected '[String: Any]' for 'metadata' key")
      throw DecodingError.typeMismatch([String: Any].self, context)
    }
    self.metadata = metadata
  }

  private enum CodingKeys: String, CodingKey {
    case id, email, metadata
  }
}

JsonContaineradalah entitas pembantu yang kami gunakan untuk membungkus decoding data JSON ke objek JSON (baik larik maupun kamus) tanpa memperluas *DecodingContainer(sehingga tidak akan mengganggu kasus yang jarang terjadi saat objek JSON tidak dimaksudkan oleh [String: Any]).

struct JsonContainer {

  let value: Any
}

extension JsonContainer: Decodable {

  public init(from decoder: Decoder) throws {
    if let keyedContainer = try? decoder.container(keyedBy: Key.self) {
      var dictionary = [String: Any]()
      for key in keyedContainer.allKeys {
        if let value = try? keyedContainer.decode(Bool.self, forKey: key) {
          // Wrapping numeric and boolean types in `NSNumber` is important, so `as? Int64` or `as? Float` casts will work
          dictionary[key.stringValue] = NSNumber(value: value)
        } else if let value = try? keyedContainer.decode(Int64.self, forKey: key) {
          dictionary[key.stringValue] = NSNumber(value: value)
        } else if let value = try? keyedContainer.decode(Double.self, forKey: key) {
          dictionary[key.stringValue] = NSNumber(value: value)
        } else if let value = try? keyedContainer.decode(String.self, forKey: key) {
          dictionary[key.stringValue] = value
        } else if (try? keyedContainer.decodeNil(forKey: key)) ?? false {
          // NOP
        } else if let value = try? keyedContainer.decode(JsonContainer.self, forKey: key) {
          dictionary[key.stringValue] = value.value
        } else {
          throw DecodingError.dataCorruptedError(forKey: key, in: keyedContainer, debugDescription: "Unexpected value for \(key.stringValue) key")
        }
      }
      value = dictionary
    } else if var unkeyedContainer = try? decoder.unkeyedContainer() {
      var array = [Any]()
      while !unkeyedContainer.isAtEnd {
        let container = try unkeyedContainer.decode(JsonContainer.self)
        array.append(container.value)
      }
      value = array
    } else if let singleValueContainer = try? decoder.singleValueContainer() {
      if let value = try? singleValueContainer.decode(Bool.self) {
        self.value = NSNumber(value: value)
      } else if let value = try? singleValueContainer.decode(Int64.self) {
        self.value = NSNumber(value: value)
      } else if let value = try? singleValueContainer.decode(Double.self) {
        self.value = NSNumber(value: value)
      } else if let value = try? singleValueContainer.decode(String.self) {
        self.value = value
      } else if singleValueContainer.decodeNil() {
        value = NSNull()
      } else {
        throw DecodingError.dataCorruptedError(in: singleValueContainer, debugDescription: "Unexpected value")
      }
    } else {
      let context = DecodingError.Context(codingPath: [], debugDescription: "Invalid data format for JSON")
      throw DecodingError.dataCorrupted(context)
    }
  }

  private struct Key: CodingKey {
    var stringValue: String

    init?(stringValue: String) {
      self.stringValue = stringValue
    }

    var intValue: Int?

    init?(intValue: Int) {
      self.init(stringValue: "\(intValue)")
      self.intValue = intValue
    }
  }
}

Perhatikan bahwa tipe angka dan boolean didukung oleh NSNumber, jika tidak, hal seperti ini tidak akan berfungsi:

if customer.metadata["keyForInt"] as? Int64 { // as it always will be nil
Alexey Kozhevnikov
sumber
Dapatkah saya mendekode hanya properti yang dipilih dan membiarkan yang lain didekodekan secara otomatis karena saya memiliki 15 properti yang mencukupi autoDecoding dan mungkin 3 yang memerlukan penanganan decoding khusus?
Michał Ziobro
@ MichałZiobro Apakah Anda ingin sebagian dari data diterjemahkan ke dalam objek JSON dan sebagian darinya diterjemahkan ke dalam variabel instan terpisah? Atau Anda bertanya tentang menulis penginisialisasi decoding parsial hanya untuk bagian dari objek (dan itu tidak memiliki kesamaan dengan struktur seperti JSON)? Sepengetahuan saya, jawaban untuk pertanyaan pertama adalah ya, untuk pertanyaan kedua adalah tidak.
Alexey Kozhevnikov
Saya hanya ingin memiliki beberapa properti dengan decoding yang disesuaikan dan sisanya dengan decoding default standar
Michał Ziobro
@ MichałZiobro Jika saya memahami Anda dengan benar, itu tidak mungkin. Bagaimanapun, pertanyaan Anda tidak relevan dengan pertanyaan SO saat ini dan layak untuk pertanyaan terpisah.
Alexey Kozhevnikov
0

decode menggunakan decoder dan kunci pengkodean

public let dataToDecode: [String: AnyDecodable]

enum CodingKeys: CodingKey {
    case dataToDecode
}

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    self.dataToDecode = try container.decode(Dictionary<String, AnyDecodable>.self, forKey: .dataToDecode) 
}    
Ashim Dahal
sumber
-1
extension ViewController {

    func swiftyJson(){
        let url = URL(string: "https://itunes.apple.com/search?term=jack+johnson")
        //let url = URL(string: "http://makani.bitstaging.in/api/business/businesses_list")

        Alamofire.request(url!, method: .get, parameters: nil).responseJSON { response in
            var arrayIndexes = [IndexPath]()
            switch(response.result) {
            case .success(_):

                let data = response.result.value as! [String : Any]

                if let responseData =  Mapper<DataModel>().map(JSON: data) {
                    if responseData.results!.count > 0{
                        self.arrayExploreStylistList = []
                    }
                    for i in 0..<responseData.results!.count{
                        arrayIndexes.append(IndexPath(row: self.arrayExploreStylistList.count + i, section: 0))
                    }
                    self.arrayExploreStylistList.append(contentsOf: responseData.results!)

                    print(arrayIndexes.count)

                }

                //                    if let arrNew = data["results"] as? [[String : Any]]{
                //                        let jobData = Mapper<DataModel>().mapArray(JSONArray: arrNew)
                //                        print(jobData)
                //                        self.datamodel = jobData
                //                    }
                self.tblView.reloadData()
                break

            case .failure(_):
                print(response.result.error as Any)
                break

            }
        }

    }
}
hiren
sumber