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"
}
非常に単純な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"
}
コード生成( http://www.json4Swift.com )を使用してjson応答からネイティブモデルを作成することをお勧めします。これにより、手作業で解析する時間が節約され、エラーのリスクが軽減されます。誤ったキーのため、すべての要素はモデルプロパティからアクセスできます。これは純粋にネイティブであり、モデルはキーをチェックするよりも意味があります。
変換は次のように簡単になります。
let userObject = UserClass(userDictionary)
print(userObject!.name)
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"
私が書いたさらに別の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?
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)
}
私は個人的には、どのサードパーティよりもネイティブの構文解析を好みます。それは、透明で魔法がないからです。 (そしてバグが少ない?)
キーとフィールドを手動でマッピングすることなく、jsonとの間で解析したい場合は、 EVReflection を使用することもできます。その後、次のようなコードを使用できます。
var user:User = User(json:jsonString)
または
var jsonString:String = user.toJsonString()
必要なことは、EVObjectをデータオブジェクトの基本クラスとして使用することだけです。より詳細なサンプルコードについては、GitHubページを参照してください。
辞書を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"]
}
}
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"
}""")!
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) }
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)")
}
Swift 4では、Decoding、CodingKeyプロトコルJSONレスポンスをデシリアライズするには:
デコード可能なプロトコルを確認するクラスを作成します
class UserInfo: Decodable
クラスのメンバーを作成する
var name: String
var email: String
var password: String
CodingKeyを継承するJSONキー列挙を作成します
enum UserInfoCodingKey: String, CodingKey { case name case password case emailId }
Initを実装する
required init(from decoder: Decoder) throws
クラス全体は次のようになります。
コールデコーダー
// jsonDataはJSONレスポンスであり、userInfoオブジェクトを取得します
let userInfo = try JsonDecoder().decode(UserInfo.self, from: jsonData)
この方法では、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
}
これを行うには、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 チュートリアル。チュートリアルをより深く掘り下げ、より良いエラー処理をカバーしているので、チュートリアルをチェックしてください。