Vraag Hoe kan ik Swift's Codable gebruiken om in een woordenboek te coderen?


Ik heb een structuur die Swift 4's implementeert Codable. Is er een eenvoudige ingebouwde manier om dat struct in een woordenboek te coderen?

let struct = Foo(a: 1, b: 2)
let dict = something(struct)
// now dict is ["a": 1, "b": 2]

39
2017-07-20 08:45


oorsprong


antwoorden:


Als u het niet erg vindt dat een beetje gegevens worden verplaatst, kunt u zoiets als dit gebruiken:

extension Encodable {
  func asDictionary() throws -> [String: Any] {
    let data = try JSONEncoder().encode(self)
    guard let dictionary = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any] else {
      throw NSError()
    }
    return dictionary
  }
}

Of een optionele variant

extension Encodable {
  var dictionary: [String: Any]? {
    guard let data = try? JSONEncoder().encode(self) else { return nil }
    return (try? JSONSerialization.jsonObject(with: data, options: .allowFragments)).flatMap { $0 as? [String: Any] }
  }
}

Ervan uitgaand Foo conformeerd naar Codable of echt Encodable dan kun je dit doen.

let struct = Foo(a: 1, b: 2)
let dict = try struct.asDictionary()
let optionalDict = struct.dictionary

Als je de andere kant op wilt (init(any)), kijk hier eens even naar Start een object conform Codable met een woordenboek / array


79
2017-09-20 18:12



Ik heb een bibliotheek gemaakt met de naam CodableFirebase en het was in eerste instantie bedoeld om het te gebruiken met Firebase Database, maar het doet eigenlijk wat je nodig hebt: het maakt een woordenboek of een ander type, net als in JSONDecoder maar je hoeft hier niet de dubbele conversie te doen zoals bij andere antwoorden. Dus het zou er ongeveer zo uitzien:

import CodableFirebase

let model = Foo(a: 1, b: 2)
let dict = try! FirebaseEncoder().encode(model)

9
2017-12-29 09:16



Ik weet niet zeker of dit de beste manier is, maar je kunt zeker iets doen als:

struct Foo: Codable {
    var a: Int
    var b: Int

    init(a: Int, b: Int) {
        self.a = a
        self.b = b
    }
}

let foo = Foo(a: 1, b: 2)
let dict = try JSONDecoder().decode([String: Int].self, from: JSONEncoder().encode(foo))
print(dict)

5
2017-07-20 09:02



Ik denk zeker dat het nuttig is om gewoon te kunnen gebruiken Codable om te coderen naar / van woordenboeken, zonder de intentie ooit JSON / Plists / whatever te raken. Er zijn tal van API's die je gewoon een woordenboek teruggeven, of een woordenboek verwachten, en het is leuk om ze gemakkelijk met Swift-structuren of objecten te kunnen verwisselen, zonder eindeloze boilerplate-code te hoeven schrijven.

Ik heb met een aantal code gespeeld op basis van de bron Foundation JSONEncoder.swift (die intern de codering / decodering van woordenboeken implementeert, maar deze niet exporteert).

De code is hier te vinden: https://github.com/elegantchaos/DictionaryCoding

Het is nog steeds vrij ruw, maar ik heb het een beetje uitgebreid, zodat het bijvoorbeeld ontbrekende waarden met standaardwaarden kan invullen bij het decoderen.


1
2018-02-21 00:02



Ik heb de PropertyListEncoder van het Swift-project naar een DictionaryEncoder, eenvoudig door de laatste serialisatie uit het woordenboek in binair formaat te verwijderen. Je kunt hetzelfde zelf doen, of je kunt mijn code overnemen hier

Het kan als volgt worden gebruikt:

do {
    let employeeDictionary: [String: Any] = try DictionaryEncoder().encode(employee)
} catch let error {
    // handle error
}

1
2018-04-02 09:56



Ik schreef snel kern om dit aan te pakken (niet met behulp van het Codable protocol). Wees voorzichtig, het type niet, controleer waarden en werkt niet recursief op waarden die codeerbaar zijn.

class DictionaryEncoder {
    var result: [String: Any]

    init() {
        result = [:]
    }

    func encode(_ encodable: DictionaryEncodable) -> [String: Any] {
        encodable.encode(self)
        return result
    }

    func encode<T, K>(_ value: T, key: K) where K: RawRepresentable, K.RawValue == String {
        result[key.rawValue] = value
    }
}

protocol DictionaryEncodable {
    func encode(_ encoder: DictionaryEncoder)
}

0
2017-08-13 05:46



let dict = try JSONSerialization.jsonObject(with: try JSONEncoder().encode(struct), options: []) as? [String: Any]


0
2017-11-30 18:30



Er is geen eenvoudige manier om dit in Codable te doen. U moet het codeerbare / decodeerbare protocol voor uw struct implementeren. Voor uw voorbeeld moet u misschien schrijven zoals hieronder

typealias EventDict = [String:Int]

struct Favorite {
    var all:EventDict
    init(all: EventDict = [:]) {
        self.all = all
    }
}

extension Favorite: Encodable {
    struct FavoriteKey: CodingKey {
        var stringValue: String
        init?(stringValue: String) {
            self.stringValue = stringValue
        }
        var intValue: Int? { return nil }
        init?(intValue: Int) { return nil }
    }

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

        for eventId in all {
            let nameKey = FavoriteKey(stringValue: eventId.key)!
            try container.encode(eventId.value, forKey: nameKey)
        }
    }
}

extension Favorite: Decodable {

    public init(from decoder: Decoder) throws {
        var events = EventDict()
        let container = try decoder.container(keyedBy: FavoriteKey.self)
        for key in container.allKeys {
            let fav = try container.decode(Int.self, forKey: key)
            events[key.stringValue] = fav
        }
        self.init(all: events)
    }
}

0
2018-05-26 16:32



Nu ik erover nadenk, heeft de vraag in het algemene geval geen antwoord, aangezien het Encodable exemplaar kan iets zijn dat niet serialiseerbaar is in een woordenboek, zoals een array:

let payload = [1, 2, 3]
let encoded = try JSONEncoder().encode(payload) // "[1,2,3]"

Anders dan dat, heb ik geschreven iets vergelijkbaars als een raamwerk.


-4
2017-08-17 08:24