[swift] Swift의 Codable을 사용하여 사전으로 인코딩하려면 어떻게해야합니까?
Swift 4를 구현하는 구조체가 Codable
있습니다. 해당 구조체를 사전으로 인코딩하는 간단한 기본 제공 방법이 있습니까?
let struct = Foo(a: 1, b: 2)
let dict = something(struct)
// now dict is ["a": 1, "b": 2]
답변
약간의 데이터 이동에 신경 쓰지 않는다면 다음과 같이 사용할 수 있습니다.
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
}
}
또는 선택적 변형
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] }
}
}
Foo
준수 한다고 가정 Codable
하거나 실제로 그렇게 Encodable
할 수 있습니다.
let struct = Foo(a: 1, b: 2)
let dict = try struct.asDictionary()
let optionalDict = struct.dictionary
다른 길로 가고 싶다면 ( init(any)
),이 Init init an object conforming to Codable with a dictionary / array를 살펴보십시오.
답변
여기서 간단하게 구현되어 DictionaryEncoder
/ DictionaryDecoder
그 포장 JSONEncoder
, JSONDecoder
및 JSONSerialization
도 전략을 디코딩 / 인코딩 처리 즉, …
class DictionaryEncoder {
private let encoder = JSONEncoder()
var dateEncodingStrategy: JSONEncoder.DateEncodingStrategy {
set { encoder.dateEncodingStrategy = newValue }
get { return encoder.dateEncodingStrategy }
}
var dataEncodingStrategy: JSONEncoder.DataEncodingStrategy {
set { encoder.dataEncodingStrategy = newValue }
get { return encoder.dataEncodingStrategy }
}
var nonConformingFloatEncodingStrategy: JSONEncoder.NonConformingFloatEncodingStrategy {
set { encoder.nonConformingFloatEncodingStrategy = newValue }
get { return encoder.nonConformingFloatEncodingStrategy }
}
var keyEncodingStrategy: JSONEncoder.KeyEncodingStrategy {
set { encoder.keyEncodingStrategy = newValue }
get { return encoder.keyEncodingStrategy }
}
func encode<T>(_ value: T) throws -> [String: Any] where T : Encodable {
let data = try encoder.encode(value)
return try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [String: Any]
}
}
class DictionaryDecoder {
private let decoder = JSONDecoder()
var dateDecodingStrategy: JSONDecoder.DateDecodingStrategy {
set { decoder.dateDecodingStrategy = newValue }
get { return decoder.dateDecodingStrategy }
}
var dataDecodingStrategy: JSONDecoder.DataDecodingStrategy {
set { decoder.dataDecodingStrategy = newValue }
get { return decoder.dataDecodingStrategy }
}
var nonConformingFloatDecodingStrategy: JSONDecoder.NonConformingFloatDecodingStrategy {
set { decoder.nonConformingFloatDecodingStrategy = newValue }
get { return decoder.nonConformingFloatDecodingStrategy }
}
var keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy {
set { decoder.keyDecodingStrategy = newValue }
get { return decoder.keyDecodingStrategy }
}
func decode<T>(_ type: T.Type, from dictionary: [String: Any]) throws -> T where T : Decodable {
let data = try JSONSerialization.data(withJSONObject: dictionary, options: [])
return try decoder.decode(type, from: data)
}
}
사용법은 JSONEncoder
/ JSONDecoder
…
let dictionary = try DictionaryEncoder().encode(object)
과
let object = try DictionaryDecoder().decode(Object.self, from: dictionary)
편의를 위해이 모든 것을 저장소에 넣었습니다. https://github.com/ashleymills/SwiftDictionaryCoding
답변
저는 CodableFirebase 라는 라이브러리를 만들었고 초기 목적은 Firebase 데이터베이스와 함께 사용하는 것이었지만 실제로 JSONDecoder
필요한 작업을 수행합니다. 에서와 같이 사전 또는 다른 유형을 생성 하지만 여기서 이중 변환을 수행 할 필요는 없습니다. 다른 답변 에서처럼. 따라서 다음과 같이 보일 것입니다.
import CodableFirebase
let model = Foo(a: 1, b: 2)
let dict = try! FirebaseEncoder().encode(model)
답변
이것이 최선의 방법인지 확실하지 않지만 확실히 다음과 같이 할 수 있습니다.
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)
답변
let dict = try JSONSerialization.jsonObject(with: try JSONEncoder().encode(struct), options: []) as? [String: Any]
답변
그렇게 할 수있는 방법은 없습니다. 위에서 답변 한대로 성능 문제가없는 경우 JSONEncoder
+ JSONSerialization
구현을 수락 할 수 있습니다 .
그러나 나는 인코더 / 디코더 객체를 제공하는 표준 라이브러리의 방식을 선호합니다.
class DictionaryEncoder {
private let jsonEncoder = JSONEncoder()
/// Encodes given Encodable value into an array or dictionary
func encode<T>(_ value: T) throws -> Any where T: Encodable {
let jsonData = try jsonEncoder.encode(value)
return try JSONSerialization.jsonObject(with: jsonData, options: .allowFragments)
}
}
class DictionaryDecoder {
private let jsonDecoder = JSONDecoder()
/// Decodes given Decodable type from given array or dictionary
func decode<T>(_ type: T.Type, from json: Any) throws -> T where T: Decodable {
let jsonData = try JSONSerialization.data(withJSONObject: json, options: [])
return try jsonDecoder.decode(type, from: jsonData)
}
}
다음 코드로 시도해 볼 수 있습니다.
struct Computer: Codable {
var owner: String?
var cpuCores: Int
var ram: Double
}
let computer = Computer(owner: "5keeve", cpuCores: 8, ram: 4)
let dictionary = try! DictionaryEncoder().encode(computer)
let decodedComputer = try! DictionaryDecoder().decode(Computer.self, from: dictionary)
나는 예제를 더 짧게 만들기 위해 여기에서 강제로 노력하고 있습니다. 프로덕션 코드에서는 오류를 적절하게 처리해야합니다.
답변
일부 프로젝트에서는 신속한 반사를 사용합니다. 그러나 중첩 된 코드화 가능한 객체는 거기에서도 매핑되지 않습니다.
let dict = Dictionary(uniqueKeysWithValues: Mirror(reflecting: foo).children.map{ ($0.label!, $0.value) })