Jak zdekodować właściwość z typem słownika JSON w protokole dekodowalnym Swift 4


104

Powiedzmy, że mam Customertyp danych, który zawiera metadatawłaściwość, która może zawierać dowolny słownik JSON w obiekcie klienta

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

metadataNieruchomość może mieć dowolną mapę obiektu JSON.

Zanim będę mógł rzutować właściwość z deserializowanego JSON, NSJSONDeserializationale z nowym Decodableprotokołem Swift 4 , nadal nie mogę wymyślić sposobu, aby to zrobić.

Czy ktoś wie, jak to osiągnąć w Swift 4 z protokołem Decodable?

Odpowiedzi:


90

Czerpiąc inspirację z tej istoty , napisałem kilka rozszerzeń dla UnkeyedDecodingContaineri KeyedDecodingContainer. Link do mojej istoty można znaleźć tutaj . Używając tego kodu, możesz teraz zdekodować dowolne Array<Any>lub Dictionary<String, Any>za pomocą znanej składni:

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

lub

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

Edycja: jest jedno zastrzeżenie, które znalazłem, które polega na dekodowaniu tablicy słowników [[String: Any]]. Wymagana składnia jest następująca. Prawdopodobnie będziesz chciał zgłosić błąd zamiast wymusić rzucanie:

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

EDYCJA 2: Jeśli chcesz po prostu przekonwertować cały plik do słownika, lepiej trzymaj się interfejsu API z JSONSerializacji, ponieważ nie znalazłem sposobu na rozszerzenie samego JSONDecodera w celu bezpośredniego dekodowania słownika.

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

Rozszerzenia

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

Interesujące, spróbuję tego sedna i zaktualizuję wynik dla Ciebie @loudmouth
Pitiphong Phongpattranont

@PitiphongPhongpattranont Czy ten kod zadziałał dla Ciebie?
loudmouth

1
@JonBrooks ostatni warunek w w UnkeyedDecodingContainer„s decode(_ type: Array<Any>.Type) throws -> Array<Any>jest sprawdzanie dla zagnieżdżonego tablicy. Więc jeśli masz strukturę danych, która wygląda następująco: [true, 452.0, ["a", "b", "c"] ] ściągnęłaby zagnieżdżoną ["a", "b", "c"]tablicę. decodePoprzez metodę UnkeyedDecodingContainer„wyskakuje” poza elementem z pojemnika. Nie powinno powodować nieskończonej rekurencji.
loudmouth

1
@loudmouth możliwe jest mają wartości zerowe dla kluczy w JSON: {"array": null}. Więc twój guard contains(key)test przejdzie, ale zawiesza się kilka wierszy później podczas próby zdekodowania wartości null dla klucza "tablica". Dlatego lepiej przed wywołaniem dodać jeszcze jeden warunek, aby sprawdzić, czy wartość faktycznie nie jest zerowa decode.
chebur

2
Znalazłem rozwiązanie: Zamiast } else if let nestedArray = try? decode(Array<Any>.self, forKey: key)próbować:} else if var nestedContainer = try? nestedUnkeyedContainer(), let nestedArray = try? nestedContainer.decode(Array<Any>.self) {
Jon Brooks

23

Ja też bawiłem się tym problemem i ostatecznie napisałem prostą bibliotekę do pracy z „ogólnymi typami JSON” . (Gdzie „ogólny” oznacza „bez struktury znanej z góry”). Głównym punktem jest reprezentacja ogólnego kodu JSON z konkretnym typem:

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

Ten typ może następnie zaimplementować Codablei Equatable.


13

Możesz stworzyć strukturę metadanych, która potwierdza Decodableprotokół i użyć JSONDecoderklasy do stworzenia obiektu z danych przy użyciu metody dekodowania, jak poniżej

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

10
Nie, nie mogę, ponieważ nie znam struktury metadatawartości. Może to być dowolny obiekt.
Pitiphong Phongpattranont

Czy masz na myśli to, że może to być tablica lub słownik?
Suhit Patil

czy możesz podać przykład lub dodać więcej wyjaśnień na temat struktury metadanych
Suhit Patil,

2
Wartością metadatamoże być dowolny obiekt JSON. Może to być pusty słownik lub dowolny słownik. "metadata": {} "metadata": {user_id: "id"} "metadata": {preference: {shows_value: true, language: "en"}} itd.
Pitiphong Phongpattranont

jedną z możliwych opcji byłoby użycie wszystkich parametrów w strukturze metadanych jako opcji i wyświetlenie wszystkich możliwych wartości w strukturze metadanych, takich jak struct metadata {var user_id: String? var preferencja: String? }
Suhit Patil

8

Przyszedłem z nieco innym rozwiązaniem.

Załóżmy, że mamy coś więcej niż prostą [String: Any]do przeanalizowania, gdyby Any była tablicą, zagnieżdżonym słownikiem lub słownikiem tablic.

Coś takiego:

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

Cóż, to jest moje rozwiązanie:

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

Wypróbuj za pomocą

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

6

Kiedy znalazłem starą odpowiedź, przetestowałem tylko prosty przypadek obiektu JSON, ale nie pusty, co spowoduje wyjątek środowiska uruchomieniowego, taki jak @slurmomatic i @zoul. Przepraszamy za ten problem.

Próbuję więc w inny sposób, mając prosty protokół JSONValue, zaimplementuj strukturę AnyJSONValuetypu erasure i używam tego typu zamiast Any. Oto implementacja.

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

A oto jak go używać podczas dekodowania

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

Problem z tą sprawą polega na tym, że musimy zadzwonić value.jsonValue as? Int . Musimy poczekać, aż Conditional Conformancewylądujemy w Swift, co rozwiąże ten problem lub przynajmniej pomoże mu być lepszym.


[Stara odpowiedź]

Publikuję to pytanie na forum Apple Developer i okazuje się, że jest to bardzo proste.

mogę zrobić

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

w inicjatorze.

Przede wszystkim źle było to przegapić.


4
Może opublikować link do pytania na Apple Developer. Anynie odpowiada, Decodablewięc nie jestem pewien, jak to jest poprawna odpowiedź.
Reza Shirazian

@RezaShirazian Tak właśnie myślałem. Ale okazuje się, że Dictionary jest zgodny z Encodable, gdy jego klucze są zgodne z Hashable i nie zależą od jego wartości. Możesz otworzyć nagłówek Dictionary i zobaczyć to sam. słownik rozszerzenia: kodowanie, gdzie Klucz: rozszerzenie z haszowaniem Słownik: możliwość dekodowania, gdzie Klucz: forums.developer.apple.com/thread/80288#237680
Pitiphong Phongpattranont

6
obecnie to nie działa. „Dictionary <String, Any> nie jest zgodny z Decodable, ponieważ Any nie jest zgodny z Decodable”
mbuchetics

Okazuje się, że to działa. Używam go w moim kodzie. Musisz zrozumieć, że nie ma sposobu, aby wyrazić wymaganie, że „Wartość słownika musi być zgodna z protokołem Decodable, aby słownik był zgodny z protokołem Decodable”. To jest „warunkowa zgodność”, która nie została jeszcze zaimplementowana w Swift 4. Myślę, że na razie jest w porządku, ponieważ istnieje wiele ograniczeń w systemie typów Swift (i typach generycznych). Więc to działa na razie, ale kiedy system typów Swift poprawi się w przyszłości (szczególnie gdy zaimplementowana jest zgodność warunkowa), to nie powinno działać.
Pitiphong Phongpattranont

3
Nie działa dla mnie od Xcode 9 beta 5. Kompiluje się, ale wybucha w czasie wykonywania: Dictionary <String, Any> nie jest zgodny z Decodable, ponieważ Any nie jest zgodny z Decodable.
zoul

6

Jeśli używasz SwiftyJSON do analizowania JSON, możesz zaktualizować do wersji 4.1.0, która Codableobsługuje protokoły. Po prostu zadeklaruj metadata: JSONi gotowe.

import SwiftyJSON

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

Nie wiem, dlaczego ta odpowiedź została odrzucona. Jest to całkowicie ważne i rozwiązuje problem.
Leonid Usov

Wydaje się, że jest dobry na migrację ze SwiftyJSON do Decodable
Michał Ziobro

To nie rozwiązuje problemu, jak następnie przeanalizować plik JSON metadanych, który był pierwotnym problemem.
llamacorn

1

Możesz rzucić okiem na 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)!)

Och, naprawdę miło. Używanie go do odbierania ogólnego JSON jako JToken, dołączanie niektórych wartości i powrót do serwera. Naprawdę bardzo dobre. To świetna robota, którą wykonałeś :)
Vitor Hugo Schwaab,

1

Najłatwiejszym i sugerowanym sposobem jest utworzenie oddzielnego modelu dla każdego słownika lub modelu w formacie JSON .

Oto co robię

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

    }
}

Stosowanie:

  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 {

        }
    }

** Użyłem opcji, aby być bezpiecznym podczas analizowania, można ją zmienić w razie potrzeby.

Przeczytaj więcej na ten temat


1
Twoja odpowiedź jest z pewnością odpowiednia dla Swift 4.1, a pierwsza linia Twojego postu jest martwa! Zakładając, że dane pochodzą z usługi internetowej. można modelować proste obiekty zagnieżdżone, a następnie przechwycić każdy z nich za pomocą składni kropkowej. Zobacz odpowiedź suhit poniżej.
David H

1

Zrobiłem kapsułę, aby ułatwić drogę kodujący dekodowanie + [String: Any], [Any]. A to zapewnia kodowanie lub dekodowanie opcjonalnych właściwości, tutaj https://github.com/levantAJ/AnyCodable

pod 'DynamicCodable', '1.0'

Jak tego użyć:

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

Tutaj jest bardziej ogólny (nie tylko [String: Any], ale[Any] można dekodować) i hermetyzowane podejście (do tego służy oddzielna jednostka) inspirowane odpowiedzią @loudmouth.

Korzystanie z niego będzie wyglądać następująco:

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

JsonContainerto jednostka pomocnicza, której używamy do zawijania dekodowania danych JSON do obiektu JSON (tablicy lub słownika) bez rozszerzania *DecodingContainer(więc nie będzie kolidować z rzadkimi przypadkami, gdy obiekt JSON nie jest rozumiany [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
    }
  }
}

Zwróć uwagę, że typy liczbowe i logiczne są NSNumberobsługiwane przez , w przeciwnym razie coś takiego nie zadziała:

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

Czy mogę zdekodować tylko wybrane właściwości i pozostawić inne zdekodowane automatycznie, ponieważ mam 15 właściwości, które wystarczają do automatycznego dekodowania, a może 3, które wymagają niestandardowej obsługi dekodowania?
Michał Ziobro

@ MichałZiobro Czy chcesz, aby część danych została zdekodowana do obiektu JSON, a część do oddzielnych zmiennych instancji? A może pytasz o napisanie inicjalizatora częściowego dekodowania tylko dla części obiektu (i nie ma to nic wspólnego ze strukturą podobną do JSON)? O ile mi wiadomo, odpowiedź na pierwsze pytanie brzmi: tak, na drugie - nie.
Alexey Kozhevnikov

Chciałbym mieć tylko niektóre właściwości z niestandardowym dekodowaniem, a resztę ze standardowym domyślnym dekodowaniem
Michał Ziobro

@ MichałZiobro Jeśli dobrze rozumiem, to nie jest możliwe. W każdym razie Twoje pytanie nie dotyczy aktualnego pytania SO i jest warte oddzielnego.
Alexey Kozhevnikov

0

dekodować za pomocą dekodera i kluczy kodujących

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

-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

            }
        }

    }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.