Swift [45] kod çözülebilir protokolünde JSON sözlüğü türü ile bir özelliğin kodu nasıl çözülür


114

Diyelim ki müşteri nesnesindeki herhangi bir JSON sözlüğünü içerebilen Customerbir metadataözelliği içeren veri türüne sahibim

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

{  
  "object": "customer",
  "id": "4yq6txdpfadhbaqnwp3",
  "email": "john.doe@example.com",
  "metadata": {
    "link_id": "linked-id",
    "buy_count": 4
  }
}

metadataMülkiyet herhangi keyfi JSON haritası nesnesi olabilir.

Özelliği serileştirilmemiş bir JSON'dan atmadan önce, NSJSONDeserializationancak yeni Swift 4 Decodableprotokolüyle, bunu yapmanın bir yolunu hala düşünemiyorum.

Çözülebilir protokollü Swift 4'te bunu nasıl başaracağını bilen var mı?

Yanıtlar:


96

Bulduğum bu özden biraz ilham alarak UnkeyedDecodingContainerve için bazı uzantılar yazdım KeyedDecodingContainer. Burada benim özüme bir bağlantı bulabilirsiniz . Bu kodu kullanarak artık herhangi bir Array<Any>veya Dictionary<String, Any>tanıdık sözdizimi ile kodunu çözebilirsiniz :

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

veya

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

Düzenleme: Bulduğum bir dizi sözlüğün kodunu çözen bir uyarı var [[String: Any]]Gerekli sözdizimi aşağıdaki gibidir. Muhtemelen zorla döküm yerine bir hata atmak isteyeceksiniz:

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

DÜZENLEME 2: Eğer tüm bir dosyayı bir sözlüğe dönüştürmek istiyorsanız, JSONSerialization'dan api'ye bağlı kalmaktan daha iyi olursunuz çünkü JSONDecoder'ın kendisini doğrudan bir sözlüğün kodunu çözmek için genişletmenin bir yolunu bulamadım.

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

Uzantılar

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

İlginç, bu ana fikri deneyeceğim ve sonucu size güncelleyeceğim @loudmouth
Pitiphong Phongpattranont

@PitiphongPhongpattranont bu kod sizin için çalıştı mı?
loudmouth

1
@JonBrooks, in UnkeyedDecodingContainer' lerdeki son koşul iç içe geçmiş bir diziyi decode(_ type: Array<Any>.Type) throws -> Array<Any>kontrol etmektir . Dolayısıyla, aşağıdakine benzer bir veri yapınız varsa: İç içe diziyi çekecektir . Bir yöntem kaptan elemanının kapalı "pop". Sonsuz özyinelemeye neden olmamalıdır. [true, 452.0, ["a", "b", "c"] ]["a", "b", "c"]decodeUnkeyedDecodingContainer
loudmouth

1
@loudmouth, json : {"array": null}. Yani sizin guard contains(key)geçecek, ancak daha sonra anahtar "dizi" için boş değerin kodunu çözmeye çalışırken birkaç satır çökecektir. Bu nedenle, çağrı yapmadan önce değerin gerçekten boş olup olmadığını kontrol etmek için bir koşul daha eklemek daha iyidir decode.
chebur

2
Bir düzeltme buldum: } else if let nestedArray = try? decode(Array<Any>.self, forKey: key)Denemek yerine :} else if var nestedContainer = try? nestedUnkeyedContainer(), let nestedArray = try? nestedContainer.decode(Array<Any>.self) {
Jon Brooks

23

Ben de bu problemle oynadım ve sonunda “jenerik JSON” türleriyle çalışmak için basit bir kütüphane yazdım . ("Genel", "önceden bilinen bir yapı olmadan" anlamına gelir.) Asıl nokta, genel JSON'u somut bir türle temsil etmektir:

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

Bu tür daha sonra Codableve uygulayabilir Equatable.


14

Aşağıdaki gibi kod çözme yöntemini kullanarak Decodableprotokolü onaylayan ve JSONDecoderverilerden nesne oluşturmak için sınıfı kullanan meta veri yapısı oluşturabilirsin

let json: [String: Any] = [
    "object": "customer",
    "id": "4yq6txdpfadhbaqnwp3",
    "email": "john.doe@example.com",
    "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)
}

12
Hayır yapamam, çünkü metadatadeğerin yapısını bilmiyorum . Herhangi bir rastgele nesne olabilir.
Pitiphong Phongpattranont

Dizi veya Sözlük türü olabileceğini mi söylüyorsunuz?
Suhit Patil

örnek verebilir veya meta veri yapısı hakkında daha fazla açıklama ekleyebilir misiniz
Suhit Patil

2
Değeri metadataherhangi bir JSON nesnesi olabilir. Yani boş bir sözlük veya herhangi bir sözlük olabilir. "meta veri": {} "meta veri": {user_id: "id"} "meta veri": {tercih: {shows_value: true, language: "en"}} vb.
Pitiphong Phongpattranont

olası bir seçenek, metadata yapısındaki tüm parametreleri isteğe bağlı olarak kullanmak ve struct meta data {var user_id: String? var tercih: Dize? }
Suhit Patil

8

Biraz farklı bir çözümle geldim.

[String: Any]Bir dizi veya iç içe geçmiş bir sözlük veya diziler sözlüğü olabilirdi, ayrıştırılması kolay bir şeyden daha fazlasına sahip olduğumuzu varsayalım .

Bunun gibi bir şey:

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

Bu benim çözümüm:

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

Kullanarak deneyin

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

7

Eski cevabı bulduğumda, yalnızca basit bir JSON nesne durumunu test ettim, ancak @slurmomatic ve @zoul gibi bir çalışma zamanı istisnasına neden olacak boş olanı değil. Bu sorun için özür dilerim.

Bu yüzden basit bir JSONValue protokolüne sahip olarak başka bir yol deniyorum, AnyJSONValuetür silme yapısını uyguluyorum ve bunun yerine bu türü kullanıyorum Any. İşte bir uygulama.

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

Ve işte kod çözerken nasıl kullanılacağı

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

Bu konudaki sorun aramamız gerektiğidir value.jsonValue as? Int. Conditional ConformanceSwift'e inene kadar beklemeliyiz , bu bu sorunu çözebilir veya en azından daha iyi olmasına yardımcı olabilir.


[Eski Cevap]

Bu soruyu Apple Developer forumunda yayınlıyorum ve çok kolay olduğu ortaya çıktı.

Yapabilirim

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

başlatıcıda.

İlk başta bunu kaçırmak benim hatamdı.


4
Apple Developer'da soruya bağlantı gönderebilir. Anyuymuyor, Decodablebu yüzden bunun nasıl doğru cevap olduğundan emin değilim.
Reza Shirazian

@RezaShirazian İlk etapta böyle düşündüm. Ancak, anahtarları Hashable ile uyumlu olduğunda ve değerlerine bağlı olmadığında Dictionary'nin Kodlanabilir'e uygun olduğu ortaya çıktı. Sözlük başlığını açıp bunu kendiniz görebilirsiniz. uzantı Sözlük: Kodlanabilir nerede Anahtar: Karıştırılabilir uzantı Sözlük: Kod çözülebilir nerede Anahtar: Hashable forums.developer.apple.com/thread/80288#237680
Pitiphong Phongpattranont

6
şu anda bu çalışmıyor. "Sözlük <Dize, Herhangi Biri> Kod Çözülebilir'e uymuyor çünkü Herhangi biri Çözülebilir ile uyumlu değil"
mbuchetics

Çalıştığı ortaya çıktı. Onu kodumda kullanıyorum. "Sözlüğün Çözülebilir protokole uyması için Sözlüğün Değerinin Çözülebilir protokole uyması gerektiği" gerekliliğini ifade etmenin bir yolu olmadığını anlamanız gerekir. Swift 4'te henüz uygulanmayan "Koşullu Uyum" budur. Swift Tipi Sisteminde (ve Jeneriklerde) birçok sınırlama olduğu için şimdilik sorun olmadığını düşünüyorum. Bu şimdilik işe yarıyor ancak gelecekte Swift Tipi Sistem geliştiğinde (özellikle Koşullu Uyum uygulandığında) bu işe yaramaz.
Pitiphong Phongpattranont

3
Xcode 9 beta 5'ten itibaren benim için çalışmıyor. Derliyor, ancak çalışma zamanında patlıyor
zoul

6

Eğer kullanırsanız SwiftyJSON JSON ayrıştırmak için aşağıdakileri yapmanız güncelleyebilirsiniz 4.1.0 sahiptir Codableprotokol desteği. Sadece beyan edin metadata: JSONve hazırsınız.

import SwiftyJSON

struct Customer {
  let id: String
  let email: String
  let metadata: JSON
}

Bu cevaba neden olumsuz oy verildiğini bilmiyorum. Tamamen geçerlidir ve sorunu çözer.
Leonid Usov

SwiftyJSON'dan Çözülebilir'e geçiş için iyi görünüyor
Michał Ziobro

Bu, orijinal sorun olan meta veri json'un nasıl ayrıştırılacağını çözmez.
lamacorn

1

BeyovaJSON'a bir göz atabilirsiniz

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

Ooh, gerçekten güzel. JToken olarak jenerik bir JSON almak, bazı değerler eklemek ve sunucuya geri dönmek için kullanmak. Gerçekten çok iyi. Yaptığın harika bir iş :)
Vitor Hugo Schwaab

1

Ben çözme + kodlayan yolu kolaylaştırmak için bir bölmeyi yaptık [String: Any], [Any]. Bu, isteğe bağlı özelliklerin kodlanmasını veya kodunun çözülmesini sağlar, burada https://github.com/levantAJ/AnyCodable

pod 'DynamicCodable', '1.0'

Bu nasıl kullanılır:

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

0

En kolay ve önerilen yol, JSON'daki her sözlük veya model için ayrı bir model oluşturmaktır .

İşte yaptığım şey

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

    }
}

Kullanım:

  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 {

        }
    }

** Ayrıştırırken güvenli tarafta olmak için isteğe bağlı kullandım, gerektiğinde değiştirilebilir.

Bu konu hakkında daha fazlasını okuyun


1
Cevabınız kesinlikle Swift 4.1 için uygun olan ve gönderinizin ilk satırı bitmiş durumda! Verilerin bir web hizmetinden geldiğini varsayarsak. basit iç içe geçmiş nesneleri modelleyebilir, ardından her birini yakalamak için nokta sözdizimini kullanabilirsiniz. Aşağıdaki suhit'in cevabına bakın.
David H

0

Burada daha genel (sadece [String: Any]değil [Any], kodu çözülebilir) ve kapsüllenmiş yaklaşım (bunun için ayrı varlık kullanılır) @loudmouth cevabından esinlenilmiştir.

Kullanmak şöyle görünecek:

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

JsonContainerkod çözme JSON verilerini genişletmeden JSON nesnesine (dizi veya sözlük) sarmak için kullandığımız yardımcı bir varlıktır *DecodingContainer(bu nedenle, bir JSON nesnesinin kastedilmediği nadir durumlara müdahale etmez [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
    }
  }
}

Sayısal ve mantıksal türlerin desteklendiğini unutmayın NSNumber, aksi takdirde bunun gibi bir şey çalışmayacaktır:

if customer.metadata["keyForInt"] as? Int64 { // as it always will be nil

Otomatik kod çözme için yeterli 15 özelliğe ve bazı özel kod çözme işlemine ihtiyaç duyan 3 özelliğe sahip olduğum için yalnızca seçilen özelliklerin kodunu çözebilir ve diğerlerini otomatik olarak çözülmüş olarak bırakabilir miyim?
Michał Ziobro

@ MichałZiobro Verinin bir kısmının JSON nesnesine çözülmesini ve bir kısmının da ayrı örnek değişkenlerine çözülmesini istiyor musunuz? Ya da sadece nesnenin bir kısmı için kısmi kod çözme başlatıcı yazmayı mı istiyorsunuz (ve JSON benzeri yapı ile ortak bir yanı yok)? Bildiğim kadarıyla ilk sorunun cevabı evet, ikinciye ise hayır.
Alexey Kozhevnikov

Yalnızca özelleştirilmiş kod çözme ve geri kalanı standart varsayılan kod çözme ile bazı özelliklere sahip olmak istiyorum
Michał Ziobro

@ MichałZiobro Seni doğru anlıyorsam bu mümkün değil. Her neyse, sorunuz mevcut SO sorusuyla alakalı değil ve ayrı bir soruya değer.
Alexey Kozhevnikov

0

kod çözücü ve kodlama anahtarlarını kullanarak kod çözme

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

0

Detaylar

  • Xcode 12.0.1 (12A7300)
  • Swift 5.3

Tai Le kitaplığına dayalı

// code from: https://github.com/levantAJ/AnyCodable/blob/master/AnyCodable/DecodingContainer%2BAnyCollection.swift

private
struct AnyCodingKey: CodingKey {
    let stringValue: String
    private (set) var intValue: Int?
    init?(stringValue: String) { self.stringValue = stringValue }
    init?(intValue: Int) {
        self.intValue = intValue
        stringValue = String(intValue)
    }
}

extension KeyedDecodingContainer {

    private
    func decode(_ type: [Any].Type, forKey key: KeyedDecodingContainer<K>.Key) throws -> [Any] {
        var values = try nestedUnkeyedContainer(forKey: key)
        return try values.decode(type)
    }

    private
    func decode(_ type: [String: Any].Type, forKey key: KeyedDecodingContainer<K>.Key) throws -> [String: Any] {
        try nestedContainer(keyedBy: AnyCodingKey.self, forKey: key).decode(type)
    }

    func decode(_ type: [String: Any].Type) throws -> [String: Any] {
        var dictionary: [String: Any] = [:]
        for key in allKeys {
            if try decodeNil(forKey: key) {
                dictionary[key.stringValue] = NSNull()
            } else if let bool = try? decode(Bool.self, forKey: key) {
                dictionary[key.stringValue] = bool
            } else if let string = try? decode(String.self, forKey: key) {
                dictionary[key.stringValue] = string
            } else if let int = try? decode(Int.self, forKey: key) {
                dictionary[key.stringValue] = int
            } else if let double = try? decode(Double.self, forKey: key) {
                dictionary[key.stringValue] = double
            } else if let dict = try? decode([String: Any].self, forKey: key) {
                dictionary[key.stringValue] = dict
            } else if let array = try? decode([Any].self, forKey: key) {
                dictionary[key.stringValue] = array
            }
        }
        return dictionary
    }
}

extension UnkeyedDecodingContainer {
    mutating func decode(_ type: [Any].Type) throws -> [Any] {
        var elements: [Any] = []
        while !isAtEnd {
            if try decodeNil() {
                elements.append(NSNull())
            } else if let int = try? decode(Int.self) {
                elements.append(int)
            } else if let bool = try? decode(Bool.self) {
                elements.append(bool)
            } else if let double = try? decode(Double.self) {
                elements.append(double)
            } else if let string = try? decode(String.self) {
                elements.append(string)
            } else if let values = try? nestedContainer(keyedBy: AnyCodingKey.self),
                let element = try? values.decode([String: Any].self) {
                elements.append(element)
            } else if var values = try? nestedUnkeyedContainer(),
                let element = try? values.decode([Any].self) {
                elements.append(element)
            }
        }
        return elements
    }
}

Çözüm

struct DecodableDictionary: Decodable {
    typealias Value = [String: Any]
    let dictionary: Value?
    init(from decoder: Decoder) throws {
        dictionary = try? decoder.container(keyedBy: AnyCodingKey.self).decode(Value.self)
    }
}

Kullanım

struct Model: Decodable {
    let num: Double?
    let flag: Bool?
    let dict: DecodableDictionary?
    let dict2: DecodableDictionary?
    let dict3: DecodableDictionary?
}

let data = try! JSONSerialization.data(withJSONObject: dictionary)
let object = try JSONDecoder().decode(Model.self, from: data)
print(object.dict?.dictionary)      // prints [String: Any]
print(object.dict2?.dictionary)     // prints nil
print(object.dict3?.dictionary)     // prints nil

-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

            }
        }

    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.