为了安全/方便,我试图将JSON解析成类型化的类,但事实证明它非常笨重。我找不到一个图书馆,甚至找不到斯威夫特的帖子(贾斯特就像我得到的一样近)。这里有一个虚构的小片段来说明:
// From NSJSONSerialization or similar and casted to an appropriate toplevel type (e.g. Dictionary).
var parsedJson: Dictionary<String, AnyObject> = [ "int" : 1, "nested" : [ "bool" : true ] ]
class TypedObject {
let stringValueWithDefault: String = ""
let intValueRequired: Int
let nestedBoolBroughtToTopLevel: Bool = false
let combinedIntRequired: Int
init(fromParsedJson json: NSDictionary) {
if let parsedStringValue = json["string"] as? String {
self.stringValueWithDefault = parsedStringValue
}
if let parsedIntValue = json["int"] as? Int {
self.intValueRequired = parsedIntValue
} else {
// Raise an exception...?
}
// Optional-chaining is actually pretty nice for this; it keeps the blocks from nesting absurdly.
if let parsedBool = json["nested"]?["bool"] as? Bool {
self.nestedBoolBroughtToTopLevel = parsedBool
}
if let parsedFirstInt = json["firstInt"] as? Int {
if let parsedSecondInt = json["secondInt"] as? Int {
self.combinedIntRequired = parsedFirstInt * parsedSecondInt
}
}
// Most succinct way to error if we weren't able to construct self.combinedIntRequired?
}
}
TypedObject(fromParsedJson: parsedJson)这里有很多我希望解决的问题:
if-let中。as?转换将失败并简单地跳过块,对用户来说信息就不多了)。let块。(这可能是将多个选项组合成一个值的一个更普遍的问题)。通常,当我觉得我应该能够做一些更具声明性的事情时,我正在编写命令式解析逻辑(或者使用一些声明的JSON模式,或者至少从类定义中推断模式)。
发布于 2014-06-18 00:12:36
我使用贾斯特框架来完成这个任务:
1)实现具有返回NSDictionary响应的单个函数的协议:
protocol APIProtocol {
func didReceiveResponse(results: NSDictionary)
}2)创建一个API类,该类定义一个NSURLConnection对象,该对象可用作iOS网络API的请求URL。创建这个类只是为了从itunes.apple.com API返回一个有效负载。
class API: NSObject {
var data: NSMutableData = NSMutableData()
var delegate: APIProtocol?
func searchItunesFor(searchTerm: String) {
// Clean up the search terms by replacing spaces with +
var itunesSearchTerm = searchTerm.stringByReplacingOccurrencesOfString(" ", withString: "+",
options: NSStringCompareOptions.CaseInsensitiveSearch, range: nil)
var escapedSearchTerm = itunesSearchTerm.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
var urlPath = "https://itunes.apple.com/search?term=\(escapedSearchTerm)&media=music"
var url: NSURL = NSURL(string: urlPath)
var request: NSURLRequest = NSURLRequest(URL: url)
var connection: NSURLConnection = NSURLConnection(request: request, delegate: self, startImmediately: false)
println("Search iTunes API at URL \(url)")
connection.start()
}
// NSURLConnection Connection failed.
func connection(connection: NSURLConnection!, didFailWithError error: NSError!) {
println("Failed with error:\(error.localizedDescription)")
}
// New request so we need to clear the data object.
func connection(didReceiveResponse: NSURLConnection!, didReceiveResponse response: NSURLResponse!) {
self.data = NSMutableData()
}
// Append incoming data.
func connection(connection: NSURLConnection!, didReceiveData data: NSData!) {
self.data.appendData(data)
}
// NSURLConnection delegate function.
func connectionDidFinishLoading(connection: NSURLConnection!) {
// Finished receiving data and convert it to a JSON object.
var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data,
options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
delegate?.didReceiveResponse(jsonResult)
}
}3)创建一个具有从Jastor继承的相关属性的类
NSDictionary响应:
{
"resultCount" : 50,
"results" : [
{
"collectionExplicitness" : "notExplicit",
"discCount" : 1,
"artworkUrl60" : "http:\/\/a4.mzstatic.com\/us\/r30\/Features\/2a\/b7\/da\/dj.kkirmfzh.60x60-50.jpg",
"collectionCensoredName" : "Changes in Latitudes, Changes in Attitudes (Ultmate Master Disk Gold CD Reissue)"
}
]
}Music.swift
class Music : Jastor {
var resultCount: NSNumber = 0
}4)然后在ViewController中,确保将委托设置为self,然后调用API的searchITunesFor()方法。
var api: API = API()
override func viewDidLoad() {
api.delegate = self;
api.searchItunesFor("Led Zeppelin")
}5)实现了didReceiveResponse()的代理方法。Jastor扩展您的类以设置从NSDictionary API返回的结果的iTunes。
// #pragma - API Delegates
func didReceiveResponse(results: NSDictionary) {
let music = Music(dictionary: results)
println(music)
}发布于 2014-06-18 04:10:54
短版本:由于init不允许失败,验证必须发生在它之外。在这些情况下,选项似乎是用于流控制的预期工具。我的解决方案是使用一个工厂方法,它返回类的一个可选项,并在其中使用选项链接来提取和验证字段。
还请注意,Int和Bool不是AnyObject的子级;来自NSDictionary的数据将以NSNumber的形式存储,不能直接转换为Swift类型。因此,对.integerValue和.boolValue的调用。
长篇版本:
// Start with NSDictionary since that's what NSJSONSerialization will give us
var invalidJson: NSDictionary = [ "int" : 1, "nested" : [ "bool" : true ] ]
var validJson: NSDictionary = [
"int" : 1,
"nested" : [ "bool" : true ],
"firstInt" : 3,
"secondInt" : 5
]
class TypedObject {
let stringValueWithDefault: String = ""
let intValueRequired: Int
let nestedBoolBroughtToTopLevel: Bool = false
let combinedIntRequired: Int
init(intValue: Int, combinedInt: Int, stringValue: String?, nestedBool: Bool?) {
self.intValueRequired = intValue
self.combinedIntRequired = combinedInt
// Use Optionals for the non-required parameters so
// we know whether to leave the default values in place
if let s = stringValue {
self.stringValueWithDefault = s
}
if let n = nestedBool {
self.nestedBoolBroughtToTopLevel = n
}
}
class func createFromDictionary(json: Dictionary<String, AnyObject>) -> TypedObject? {
// Validate required fields
var intValue: Int
if let x = (json["int"]? as? NSNumber)?.integerValue {
intValue = x
} else {
return nil
}
var combinedInt: Int
let firstInt = (json["firstInt"]? as? NSNumber)?.integerValue
let secondInt = (json["secondInt"]? as? NSNumber)?.integerValue
switch (firstInt, secondInt) {
case (.Some(let first), .Some(let second)):
combinedInt = first * second
default:
return nil
}
// Extract optional fields
// For some reason the compiler didn't like casting from AnyObject to String directly
let stringValue = json["string"]? as? NSString as? String
let nestedBool = (json["nested"]?["bool"]? as? NSNumber)?.boolValue
return TypedObject(intValue: intValue, combinedInt: combinedInt, stringValue: stringValue, nestedBool: nestedBool)
}
class func createFromDictionary(json: NSDictionary) -> TypedObject? {
// Manually doing this cast since it works, and the only thing Apple's docs
// currently say about bridging Cocoa and Dictionaries is "Information forthcoming"
return TypedObject.createFromDictionary(json as Dictionary<String, AnyObject>)
}
}
TypedObject.createFromDictionary(invalidJson) // nil
TypedObject.createFromDictionary(validJson) // it works!发布于 2014-06-24 06:57:06
我还完成了以下转换:
class Image {
var _id = String()
var title = String()
var subTitle = String()
var imageId = String()
func toDictionary(dict dictionary: NSDictionary) {
self._id = dictionary["_id"] as String
self.title = dictionary["title"] as String
self.subTitle = dictionary["subTitle"] as String
self.imageId = dictionary["imageId"] as String
}
func safeSet(d: NSMutableDictionary, k: String, v: String) {
if (v != nil) {
d[k] = v
}
}
func toDictionary() -> NSDictionary {
let jsonable = NSMutableDictionary()
self.safeSet(jsonable, k: "title", v: self.title);
self.safeSet(jsonable, k: "subTitle", v: self.subTitle);
self.safeSet(jsonable, k: "imageId", v: self.imageId);
return jsonable
}
}然后我简单地做以下几点:
// data (from service)
let responseArray = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error: nil) as NSArray
self.objects = NSMutableArray()
for item: AnyObject in responseArray {
var image = Image()
image.toDictionary(dict: item as NSDictionary)
self.objects.addObject(image)
}如果您想发布数据:
var image = Image()
image.title = "title"
image.subTitle = "subTitle"
image.imageId = "imageId"
let data = NSJSONSerialization.dataWithJSONObject(image.toDictionary(), options: .PrettyPrinted, error: nil) as NSData
// data (to service)
request.HTTPBody = data;https://stackoverflow.com/questions/24235673
复制相似问题