programing

Swift 개체를 사전으로 변환하는 방법

powerit 2023. 2. 27. 22:14
반응형

Swift 개체를 사전으로 변환하는 방법

iOS 프로그래밍은 비교적 초보입니다.단, Swift는 오브젝트를 JSON으로 자동 변환하는 방법을 가지고 있을 것으로 생각됩니다.그러나, 나는 이것을 할 수 있는 라이브러리를 몇 개 찾았습니다.

하지만...

웹 서비스에 데이터를 어떻게 게시하든(Alamo Fire 같은 것을 사용해도) 사전이 요구되어야 하는 것 같습니다.이러한 포럼에서는 반환된 JSON 문자열을 개체로 쉽게 변환할 수 있음을 보여 줍니다.맞아요, 하지만 요청은 수동으로 코딩해야 해요즉, 모든 개체 속성을 살펴보고 사전으로 매핑합니다.

제 질문은 다음과 같습니다.내가 뭘 빼놓았나요?제가 잘못 알고 있는 건가요? (a) REQUEST에서 (사전 대신) JSON을 전송하거나 (b) 객체를 사전으로 자동 변환하는 매우 쉬운 방법이 있나요?

JSON 응답에 대처하는 것이 얼마나 쉬운지 알 수 있습니다.웹 서비스에 게시할 요청 개체를 Alamo Fire와 같은 라이브러리에 필요한 형식으로 자동 변환하는 방법을 찾고 있습니다.다른 언어에서는 이것은 매우 사소한 일이기 때문에 Swift에서도 마찬가지로 쉽고 자동화된 방법이 있었으면 합니다.

@Darko의 의견에 동의하지 않을 수 없습니다.

Swift 2에서는

프로토콜 지향 프로그래밍과 Mirror 클래스에서 제공하는 단순 리플렉션을 사용합니다.

protocol JSONAble {}

extension JSONAble {
    func toDict() -> [String:Any] {
        var dict = [String:Any]()
        let otherSelf = Mirror(reflecting: self)
        for child in otherSelf.children {
            if let key = child.label {
                dict[key] = child.value
            }
        }
        return dict
    }
}

그러면 이 프로토콜을 요청 클래스와 함께 사용하여 원하는 사전을 만들 수 있습니다.

class JsonRequest : JSONAble {
    var param1 : String?
    // ...
}

let request = JsonRequest()
// set params of the request
let dict = request.toDict()
// use your dict

이에 대한 저의 해결책은 다음과 같습니다.

extension Encodable {

    var dict : [String: Any]? {
        guard let data = try? JSONEncoder().encode(self) else { return nil }
        guard let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String:Any] else { return nil }
        return json
    }
}

용도는 다음과 같습니다.

movies.compactMap { $0.dict }

Swift는 현재 Java나 C#과 같은 고급 리플렉션을 지원하지 않기 때문에 정답은 다음과 같습니다. 즉, 순수 Swift에는 똑같이 쉽고 자동화된 방법이 없습니다.

[갱신] 스위프트4는 그동안CodableJSON 및 PLIST와의 시리얼화를 허용하는 프로토콜입니다.

typealias Codable = Decodable & Encodable

반사를 사용하지 않고 중첩된 객체에 대해 작동합니다(Swift 4).

protocol Serializable {
    var properties:Array<String> { get }
    func valueForKey(key: String) -> Any?
    func toDictionary() -> [String:Any]
}

extension Serializable {
    func toDictionary() -> [String:Any] {
        var dict:[String:Any] = [:]

        for prop in self.properties {
            if let val = self.valueForKey(key: prop) as? String {
                dict[prop] = val
            } else if let val = self.valueForKey(key: prop) as? Int {
                dict[prop] = val
            } else if let val = self.valueForKey(key: prop) as? Double {
                dict[prop] = val
            } else if let val = self.valueForKey(key: prop) as? Array<String> {
                dict[prop] = val
            } else if let val = self.valueForKey(key: prop) as? Serializable {
                dict[prop] = val.toDictionary()
            } else if let val = self.valueForKey(key: prop) as? Array<Serializable> {
                var arr = Array<[String:Any]>()

                for item in (val as Array<Serializable>) {
                    arr.append(item.toDictionary())
                }

                dict[prop] = arr
            }
        }

        return dict
    }
}

변환할 사용자 지정 개체에 대한 속성 및 valueForKey를 구현하기만 하면 됩니다.예를 들어 다음과 같습니다.

class Question {
    let title:String
    let answer:Int

    init(title:String, answer:Int) {
        self.title = title
        self.answer = answer
    }
}
extension Question : Serializable {
    var properties: Array<String> {
        return ["title", "answer"]
    }

    func valueForKey(key: String) -> Any? {
        switch key {
        case "title":
            return title
        case "answer":
            return answer
        default:
            return nil
        }
    }
} 

필요에 따라 toDictionary 함수에 값 유형을 추가할 수 있습니다.

Stack Overflow를 여러 번 조사한 결과 발견한 최신 솔루션은 다음과 같습니다.

//This block of code used to convert object models to json string
let jsonData = try JSONEncoder().encode(requestData)
let jsonString = String(data: jsonData, encoding: .utf8)!
print(jsonString)

//This method is used to convert jsonstring to dictionary [String:Any]
func jsonToDictionary(from text: String) -> [String: Any]? {
    guard let data = text.data(using: .utf8) else { return nil }
    let anyResult = try? JSONSerialization.jsonObject(with: data, options: [])
    return anyResult as? [String: Any]
}

//Use above method something like this
let params = jsonToDictionary(from: jsonString) ?? [String : Any]()
 
//Use params to pass in paramters
Alamofire.request(completeUrl, method: .post, parameters: params, encoding:JSONEncoding.prettyPrinted, headers: myHeaders){
        
        response in

       //Do whatever you want with response of it.
        
    }

주의:

  1. 저는 이 솔루션을 여러 답변에서 조합합니다.
  2. 이 솔루션은 alamofire가 이 형식의 파라미터만 받아들이기 때문에 alamofire와 함께 사용하였습니다.(String:[임의]).

ObjectMapper 라이브러리를 사용할 수도 있습니다.오브젝트를 사전으로 변환하는 "toJSON" 메서드가 있습니다.

요컨대

let dict = Mirror(reflecting: self).children.map({ $0 }).reduce(into: [:]) { $0[$1.label] = $1.value }

미러를 특정 사전 유형으로 변환하여 사용하는 예:

protocol DictionaryConvertible { }

extension DictionaryConvertible {
    func toDictionary() -> [String: CustomStringConvertible] {
        Dictionary(
            uniqueKeysWithValues: Mirror(reflecting: self).children
                .compactMap { child in
                    if let label = child.label,
                        let value = child.value as? CustomStringConvertible {
                        return (label, value)
                    } else {
                        return nil
                    }
                }
        )
    }
}

언급URL : https://stackoverflow.com/questions/31971256/how-to-convert-a-swift-object-to-a-dictionary

반응형