Swift's 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)
사전/어레이를 사용하여 Codable에 준거한 오브젝트를 이 Init을 참조해 주세요.
다음으로 간단한 구현 예를 제시하겠습니다.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
저는 Codable Firebase라는 라이브러리를 만들었습니다.처음에는 Firebase Database와 함께 사용하는 것이 목적이었지만 실제로는 필요한 기능을 합니다.이것은 사전이나 다른 타입을 만듭니다.JSONDecoder
다른 답변에서처럼 이중 변환을 할 필요는 없습니다.그러면 다음과 같이 됩니다.
import CodableFirebase
let model = Foo(a: 1, b: 2)
let dict = try! FirebaseEncoder().encode(model)
그것을 할 수 있는 방법이 없다.위의 답변과 같이 성능 문제가 없는 경우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)
저는 이 사례를 줄이려고 억지로 노력하고 있습니다.제품 코드에서는 오류를 적절하게 처리해야 합니다.
이것이 최선의 방법인지는 모르겠지만 다음과 같은 작업을 수행할 수 있습니다.
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]
Swift 프로젝트의 PropertyListEncoder를 DictionaryEncoder로 수정했습니다.단, 사전에서 바이너리 형식으로 최종 시리얼화를 삭제하기만 하면 됩니다.네가 직접 하든가 내 코드를 가져가든
다음과 같이 사용할 수 있습니다.
do {
let employeeDictionary: [String: Any] = try DictionaryEncoder().encode(employee)
} catch let error {
// handle error
}
어떤 프로젝트에서는 빠른 반사에 익숙해져요.단, 네스트된 코드 가능한 오브젝트도 맵되지 않도록 주의해 주십시오.
let dict = Dictionary(uniqueKeysWithValues: Mirror(reflecting: foo).children.map{ ($0.label!, $0.value) })
저는 이 모든 것을 사용할 수 있다는 것이 어떤 가치가 있다고 생각합니다.Codable
JSON/Plists/무엇을 누를 생각 없이 사전에서 인코딩/인코딩할 수 있습니다.사전을 돌려주거나 사전을 기대하는 API가 풍부하고, 끝없는 보일러 플레이트 코드를 쓸 필요 없이 Swift 구조나 오브젝트와 쉽게 교환할 수 있어 좋습니다.
Foundation JSONEncoder.swift 소스(실제로 사전 인코딩/디코딩을 내부적으로 구현하지만 내보내지는 않음)에 기반한 코드를 가지고 라운드를 하고 있습니다.
코드는 https://github.com/elegantchaos/DictionaryCoding 에서 찾을 수 있습니다.
아직 대략적인 수준이지만, 예를 들어 디코딩 시 결측값을 기본값으로 채울 수 있도록 조금 확장했습니다.
다음은 프로토콜 기반 솔루션입니다.
protocol DictionaryEncodable {
func encode() throws -> Any
}
extension DictionaryEncodable where Self: Encodable {
func encode() throws -> Any {
let jsonData = try JSONEncoder().encode(self)
return try JSONSerialization.jsonObject(with: jsonData, options: .allowFragments)
}
}
protocol DictionaryDecodable {
static func decode(_ dictionary: Any) throws -> Self
}
extension DictionaryDecodable where Self: Decodable {
static func decode(_ dictionary: Any) throws -> Self {
let jsonData = try JSONSerialization.data(withJSONObject: dictionary, options: [])
return try JSONDecoder().decode(Self.self, from: jsonData)
}
}
typealias DictionaryCodable = DictionaryEncodable & DictionaryDecodable
사용 방법은 다음과 같습니다.
class AClass: Codable, DictionaryCodable {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
struct AStruct: Codable, DictionaryEncodable, DictionaryDecodable {
var name: String
var age: Int
}
let aClass = AClass(name: "Max", age: 24)
if let dict = try? aClass.encode(), let theClass = try? AClass.decode(dict) {
print("Encoded dictionary: \n\(dict)\n\ndata from decoded dictionary: \"name: \(theClass.name), age: \(theClass.age)\"")
}
let aStruct = AStruct(name: "George", age: 30)
if let dict = try? aStruct.encode(), let theStruct = try? AStruct.decode(dict) {
print("Encoded dictionary: \n\(dict)\n\ndata from decoded dictionary: \"name: \(theStruct.name), age: \(theStruct.age)\"")
}
(코드 가능한 프로토콜을 사용하지 않고) 이 문제를 처리하기 위해 간단한 요지를 작성했습니다.이 기능은 어떤 값도 유형 검사하지 않으며 인코딩 가능한 값에서도 반복적으로 작동하지 않습니다.
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)
}
코드블에서 이것을 할 수 있는 간단한 방법은 없다.구조에 Encodable/Decodable 프로토콜을 구현해야 합니다.예를 들어 다음과 같이 써야 할 수 있습니다.
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)
}
}
디코딩 및 인코딩을 용이하게 하기 위해 https://github.com/levantAJ/AnyCodable에 팟을 만들었습니다. [String: Any]
★★★★★★★★★★★★★★★★★」[Any]
pod 'DynamicCodable', '1.0'
아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아.[String: Any]
★★★★★★★★★★★★★★★★★」[Any]
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)
}
}
조사 결과 Codable & Decodable에서 상속받은 클래스에서 Any 키워드를 사용하면 오류가 발생한다는 것을 알 수 있었습니다.사전 사용자를 서버에서 가져오는 데이터 유형에 사용하려면 다음과 같이 하십시오.예를 들어, 서버가 [String : Int]유형의 사전을 송신하고 있는 경우, [String : Int]유형을 사용하면 동작하지 않습니다.
다음은 딕셔너리 -> 오브젝트입니다.스위프트 5
extension Dictionary where Key == String, Value: Any {
func object<T: Decodable>() -> T? {
if let data = try? JSONSerialization.data(withJSONObject: self, options: []) {
return try? JSONDecoder().decode(T.self, from: data)
} else {
return nil
}
}
}
이 요. 이 는 정답이 없어요.Encodable
할 수 없는 일 수 .
let payload = [1, 2, 3]
let encoded = try JSONEncoder().encode(payload) // "[1,2,3]"
언급URL : https://stackoverflow.com/questions/45209743/how-can-i-use-swift-s-codable-to-encode-into-a-dictionary
'programing' 카테고리의 다른 글
iOS: 배경이 투명한 모달 뷰 컨트롤러 (0) | 2023.04.26 |
---|---|
실행 중 PowerShell 출력을 파일로 리디렉션하는 방법 (0) | 2023.04.21 |
C++ 문자열에서 마지막 문자 제거 (0) | 2023.04.21 |
목록을 목록으로 변환 (0) | 2023.04.21 |
iPhone SDK: loadView와 viewDidLoad의 차이점은 무엇입니까? (0) | 2023.04.21 |