web-dev-qa-db-ja.com

JSON / NSDictionaryをSwiftオブジェクトにデシリアライズします

Swiftオブジェクトに対するJSONレスポンスを適切にデシリアライズする方法はありますか?固定JSON APIのコンテナとしてDTOを使用していますか?

http://james.newtonking.com/json またはJavaのこの例のようなものに似たもの

User user = jsonResponse.readEntity(User.class);

jsonResponse.toString()は次のようなものです

{
  "name": "myUser", 
  "email": "[email protected]",
  "password": "passwordHash"
}
52
dimitri

Swift 4アップデート


非常に単純なJSONオブジェクトを指定するため、そのモデルを処理するためのコードが用意されています。より複雑なJSONモデルが必要な場合は、このサンプルを改善する必要があります。

カスタムオブジェクト

class Person : NSObject {
    var name : String = ""
    var email : String = ""
    var password : String = ""

    init(JSONString: String) {
        super.init()

        var error : NSError?
        let JSONData = JSONString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)

        let JSONDictionary: Dictionary = NSJSONSerialization.JSONObjectWithData(JSONData, options: nil, error: &error) as NSDictionary

        // Loop
        for (key, value) in JSONDictionary {
            let keyName = key as String
            let keyValue: String = value as String

            // If property exists
            if (self.respondsToSelector(NSSelectorFromString(keyName))) {
                self.setValue(keyValue, forKey: keyName)
            }
        }
        // Or you can do it with using 
        // self.setValuesForKeysWithDictionary(JSONDictionary)
        // instead of loop method above
    }
}

これが、JSON文字列を使用してカスタムクラスを呼び出す方法です。

override func viewDidLoad() {
    super.viewDidLoad()
    let jsonString = "{ \"name\":\"myUser\", \"email\":\"[email protected]\", \"password\":\"passwordHash\" }"
    var aPerson : Person = Person(JSONString: jsonString)
    println(aPerson.name) // Output is "myUser"
}
60
mohacs

コード生成( http://www.json4Swift.com )を使用してjson応答からネイティブモデルを作成することをお勧めします。これにより、手作業で解析する時間が節約され、エラーのリスクが軽減されます。誤ったキーのため、すべての要素はモデルプロパティからアクセスできます。これは純粋にネイティブであり、モデルはキーをチェックするよりも意味があります。

変換は次のように簡単になります。

let userObject = UserClass(userDictionary)
print(userObject!.name)
10
Syed Absar

Swift 2:Mohacsの前の投稿が本当に好きです!よりオブジェクト指向にするために、対応する拡張機能を作成しました。

extension NSObject{       
    convenience init(jsonStr:String) {            
        self.init()

        if let jsonData = jsonStr.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
        {
            do {
                let json = try NSJSONSerialization.JSONObjectWithData(jsonData, options: []) as! [String: AnyObject]

                // Loop
                for (key, value) in json {
                    let keyName = key as String
                    let keyValue: String = value as! String

                    // If property exists
                    if (self.respondsToSelector(NSSelectorFromString(keyName))) {
                        self.setValue(keyValue, forKey: keyName)
                    }
                }

            } catch let error as NSError {
                print("Failed to load: \(error.localizedDescription)")
            }
        }
        else
        {
            print("json is of wrong format!")
        }
    }
}

カスタムクラス:

class Person : NSObject {
       var name : String?
       var email : String?
       var password : String?
}

class Address : NSObject {
       var city : String?
       var Zip : String?
}

jSON文字列を使用してカスタムクラスを呼び出す:

var jsonString = "{ \"name\":\"myUser\", \"email\":\"[email protected]\", \"password\":\"passwordHash\" }"
let aPerson = Person(jsonStr: jsonString)
print(aPerson.name!) // Output is "myUser"

jsonString = "{ \"city\":\"Berlin\", \"Zip\":\"12345\" }"
let aAddress = Address(jsonStr: jsonString)
print(aAddress.city!) // Output is "Berlin"
9
Peter Kreinz

私が書いたさらに別のJSONハンドラー:

これを使用すると、次のように移動できます。

let obj:[String:AnyObject] = [
    "array": [JSON.null, false, 0, "", [], [:]],
    "object":[
        "null":   JSON.null,
        "bool":   true,
        "int":    42,
        "double": 3.141592653589793,
        "string": "a α\t弾\n????",
        "array":  [],
        "object": [:]
    ],
    "url":"http://blog.livedoor.com/dankogai/"
]

let json = JSON(obj)

json.toString()
json["object"]["null"].asNull       // NSNull()
json["object"]["bool"].asBool       // true
json["object"]["int"].asInt         // 42
json["object"]["double"].asDouble   // 3.141592653589793
json["object"]["string"].asString   // "a α\t弾\n????"
json["array"][0].asNull             // NSNull()
json["array"][1].asBool             // false
json["array"][2].asInt              // 0
json["array"][3].asString           // ""

ご覧のとおり、添え字間に!?は必要ありません。

さらに、次のように独自のスキーマを適用できます。

//// schema by subclassing
class MyJSON : JSON {
    override init(_ obj:AnyObject){ super.init(obj) }
    override init(_ json:JSON)  { super.init(json) }
    var null  :NSNull? { return self["null"].asNull }
    var bool  :Bool?   { return self["bool"].asBool }
    var int   :Int?    { return self["int"].asInt }
    var double:Double? { return self["double"].asDouble }
    var string:String? { return self["string"].asString }
    var url:   String? { return self["url"].asString }
    var array :MyJSON  { return MyJSON(self["array"])  }
    var object:MyJSON  { return MyJSON(self["object"]) }
}

let myjson = MyJSON(obj)
myjson.object.null      // NSNull?
myjson.object.bool      // Bool?
myjson.object.int       // Int?
myjson.object.double    // Double?
myjson.object.string    // String?
myjson.url              // String?
7
dankogai

Apple 2.0でJSONをデシリアライズするための 素晴らしい例 by Swiftがあります

秘Theは、guardキーワードを使用して、次のように割り当てを連鎖させることです。

init?(attributes: [String : AnyObject]) {
    guard let name = attributes["name"] as? String,
        let coordinates = attributes["coordinates"] as? [String: Double],
        let latitude = coordinates["lat"],
        let longitude = coordinates["lng"],
        else {
            return nil
    }
    self.name = name
    self.coordinates = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
}

私は個人的には、どのサードパーティよりもネイティブの構文解析を好みます。それは、透明で魔法がないからです。 (そしてバグが少ない?)

4
AmitP

キーとフィールドを手動でマッピングすることなく、jsonとの間で解析したい場合は、 EVReflection を使用することもできます。その後、次のようなコードを使用できます。

var user:User = User(json:jsonString)

または

var jsonString:String = user.toJsonString()

必要なことは、EVObjectをデータオブジェクトの基本クラスとして使用することだけです。より詳細なサンプルコードについては、GitHubページを参照してください。

3
Edwin Vermeer

辞書をSwiftオブジェクトにすばやく簡単に逆シリアル化できるこの小さなオープンソースライブラリを最近作成しました。 https://github.com/isair/JSONHelper

これを使用すると、データの逆シリアル化が次のように簡単になります。

var myInstance = MyClass(data: jsonDictionary)

または

myInstance <-- jsonDictionary

そして、モデルは次のように見える必要があります:

struct SomeObjectType: Deserializable {
    var someProperty: Int?
    var someOtherProperty: AnotherObjectType?
    var yetAnotherProperty: [YetAnotherObjectType]?

    init(data: [String: AnyObject]) {
        someProperty <-- data["some_key"]
        someOtherProperty <-- data["some_other_key"]
        yetAnotherProperty <-- data["yet_another_key"]
    }
}

あなたの場合、次のようになります:

struct Person: Deserializable {
    var name: String?
    var email: String?
    var password: String?

    init(data: [String: AnyObject]) {
        name <-- data["name"]
        email <-- data["email"]
        password <-- data["password"]
    }
}
3
isair

quicktype を使用して、サンプルからモデルとシリアル化ヘルパーを生成しました。

import Foundation

struct User: Codable {
    let name: String
    let email: String
    let password: String
}

extension User {
    static func from(json: String, using encoding: String.Encoding = .utf8) -> OtherUser? {
        guard let data = json.data(using: encoding) else { return nil }
        return OtherUser.from(data: data)
    }

    static func from(data: Data) -> OtherUser? {
        let decoder = JSONDecoder()
        return try? decoder.decode(OtherUser.self, from: data)
    }

    var jsonData: Data? {
        let encoder = JSONEncoder()
        return try? encoder.encode(self)
    }

    var jsonString: String? {
        guard let data = self.jsonData else { return nil }
        return String(data: data, encoding: .utf8)
    }
}

次に、次のようにUser値を解析します。

let user = User.from(json: """{
  "name": "myUser", 
  "email": "[email protected]",
  "password": "passwordHash"
}""")!
3
David Siegel

HandyJSONは、JSONを処理する別のオプションです。 https://github.com/alibaba/handyjson

JSONを直接オブジェクトにデシリアライズします。マッピング関係を指定する必要も、NSObjectから継承する必要もありません。 pure-Swiftクラス/構造を定義し、JSONをデシリアライズするだけです。

 class Animal:HandyJSON {
 var name:String?
 var id:String?
 var num:Int?
 
必要なinit(){} 
} 
 
 let jsonString = "{\" name\":\" cat\"、\" id\":\" 12345\" 、\ "num \":180} "
 
 let let animal = JSONDeserializer.deserializeFrom(jsonString){
 print(animal)
} 
3
xuyecan

MohacsとPeter Kreinzの優れた回答を少し拡張して、各オブジェクトに有効なJSONデータ型が混在している類似オブジェクトの配列をカバーします。 JSONデータの解析がJSONデータ型の混合を含む類似オブジェクトの配列である場合、JSONデータを解析するためのdoループはこれになります。

// Array of parsed objects
var parsedObjects = [ParsedObject]()
do {
    let json = try NSJSONSerialization.JSONObjectWithData(jsonData, options: []) as [Dictionary<String, AnyObject>]
    // Loop through objects
    for dict in json {
        // ParsedObject is a single instance of an object inside the JSON data
        // Its properties are a mixture of String, Int, Double and Bool
        let parsedObject = ParsedObject()
        // Loop through key/values in object parsed from JSON
        for (key, value) in json {
            // If property exists, set the value
            if (parsedObject.respondsToSelector(NSSelectorFromString(keyName))) {
                // setValue can handle AnyObject when assigning property value
                parsedObject.setValue(keyValue, forKey: keyName)
            }
        }
        parsedObjects.append(parsedObject)
    }
} catch let error as NSError {
    print("Failed to load: \(error.localizedDescription)")
}
2
jkwuc89

Swift 4では、DecodingCodingKeyプロトコルJSONレスポンスをデシリアライズするには:

  1. デコード可能なプロトコルを確認するクラスを作成します

    class UserInfo: Decodable

  2. クラスのメンバーを作成する

    var name: String

    var email: String

    var password: String

  3. CodingKeyを継承するJSONキー列挙を作成します

    enum UserInfoCodingKey: String, CodingKey { case name case password case emailId }

  4. Initを実装する

    required init(from decoder: Decoder) throws

    クラス全体は次のようになります。

    enter image description here

  5. コールデコーダー

    // jsonDataはJSONレスポンスであり、userInfoオブジェクトを取得します

    let userInfo = try JsonDecoder().decode(UserInfo.self, from: jsonData)

2
Yogesh Bharate

この方法では、URLからユーザーを取得できます。 NSDataをNSDictionaryに解析してから、NSObjectに解析します。

let urlS = "http://api.localhost:3000/"

func getUser(username: Strung) -> User {
   var user = User()
   let url = NSURL(string: "\(urlS)\(username)")
   if let data = NSData(contentsOfURL: url!) {
     setKeysAndValues(user, dictionary: parseData(data))
   }
   return user
}

func setKeysAndValues (object : AnyObject, dictionary : NSDictionary)  -> AnyObject  {
    for (key, value) in dictionary {
        if let key = key  as? String, let value = value as? String {
            if (object.respondsToSelector(NSSelectorFromString(key))) {
                object.setValue(value, forKey: key)
            }
        }
    }
    return object
}

func parseData (data : NSData)  -> NSDictionary  {
    var error: NSError?
    return NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &error) as! NSDictionary
}
1
Eduardo Irias

これを行うには、NSJSONSerializationを使用します。 dataはJSONです。

最初にifステートメントでラップして、エラー処理機能を提供します

if let data = data,
 json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [String: AnyObject] {
// Do stuff
} else {
  // Do stuff
  print("No Data :/")
}

それらを割り当てます:

let email = json["email"] as? String
let name = json["name"] as? String
let password = json["password"] as? String

さて、これは結果を表示します:

print("Found User iname: \(name) with email: \(email) and pass \(password)")

これから引用 Swift Parse JSON チュートリアル。チュートリアルをより深く掘り下げ、より良いエラー処理をカバーしているので、チュートリアルをチェックしてください。

0
MarkP