Mac
iOS
Swift

【swift】JSONをDictionary/NSDictionaryで扱いたくないので、ちょっと検討してみたメモ

More than 3 years have passed since last update.

swiftにおいて、jsonの扱い方については、すでに、

http://qiita.com/yukihamada/items/9b0067f905418105a2c6

http://qiita.com/dankogai/items/e3d9882f4d25bc44cc03

http://qiita.com/g08m11/items/c3746cbdbdb4c7b71607

http://qiita.com/Ushio@github/items/c5f1127947d6740737f4

という記事で紹介されているように、

いくつかクラス、構造体、列挙型が紹介されてまして、基本的には

それらを使えばいいのかなというところですが、

せっかくなので、swiftでの実装の試しということで、これらのソースを眺めながら

プロトコルやらの扱いをやってみたかったので、個人的に考えてみた。


目的

外部APIなどのレスポンスとしてJSONを扱う場合、

受け取った、NSDataを、NSJSONSerializationで変換する事が多いので、

それの代わりとしたい。


ひとまず構造体として進める。

public struct JSONObject {

private var _jsondata:AnyObject?;

//newvalue などで利用することもあるので
public var jsondata:AnyObject!{
get{
return _jsondata;
}
}

//NSJSONSerialization.JSONObjectWithDataと同等の引数を用意
//外部からのNSDataを利用する。
public init(data:NSData, options opt: NSJSONReadingOptions = .AllowFragments, error: NSErrorPointer = nil) {
_jsondata = NSJSONSerialization.JSONObjectWithData(data, options: opt, error: error);
}

//json内各々のデータを扱うため
public init(_ jsondata: AnyObject!) {
_jsondata = jsondata
}


indexやkeyを指定してのデータの取得を行いたい。

扱いとして、jsonのデータを扱う場合において、

json[0]["hoge"]

のような形で扱いたい。

その場合、

subscriptの実装が必要になります。

また、クラッシュしないようにも考慮したいと思います。

クラッシュうんぬんに関しては、optional chaining で回避したい。

Arrayの場合


public subscript(index: Int) -> JSONObject {
get {
if index < _jsondata?.count {
return JSONObject(_jsondata?[index])
}
return JSONObject(nil);
}
set {
if index < _jsondata?.count {
var array:[AnyObject]! = _jsondata as [AnyObject]
if array != nil {
array[index] = newValue.jsondata
_jsondata = array
}
}
}
}

Dictionayの場合


public subscript(key: String) -> JSONObject {
get {
if let val:AnyObject? = _jsondata?[key] {
return JSONObject(val)
} else {
return JSONObject(nil);
}
}
set {
var dic:[String : AnyObject]! = _jsondata as [String : AnyObject]!
if dic != nil {
dic[key] = newValue.jsondata
_jsondata = dic
}
}
}


データの出力

json[0]["hoge"]としてデータのアクセスができたとしても、

あくまでJSONObjectのため、printlnで出力させたとしても、

JSONObject

と表示される。

そこで、

Printableに準拠させて

出力を調整する。


extension JSONObject: Printable {
public var description: String {
return toString();
}

private func toString() -> String{
if let value: AnyObject = _jsondata {
var ret:String = "";
switch value {
case let number as NSNumber:
ret = number.stringValue
case let string as NSString:
ret = string
case let null as NSNull:
ret = "NSNull"
case let array as [AnyObject]:
var data:NSData! = NSJSONSerialization.dataWithJSONObject(array,
options:NSJSONWritingOptions.PrettyPrinted,
error:nil)
if data != nil {
ret = NSString(data: data!, encoding: NSUTF8StringEncoding)!
}
case let dic as [String : AnyObject]:
var data:NSData! = NSJSONSerialization.dataWithJSONObject(dic,
options:NSJSONWritingOptions.PrettyPrinted,
error:nil)
if data != nil {
ret = NSString(data: data!, encoding: NSUTF8StringEncoding)!
}
default:
//unknown
ret = "unknown";
}
return ret;
} else {
return "";
}
}
}


データの取得

様々な型について考慮が必要ですが、

ひとまずString/Array/Dictionaryだけで。。


extension JSONObject {
public var string:String?{
get {
if let value: AnyObject = _jsondata {
switch(value) {
case let string as NSString:
return string
default:
return nil
}
}
return nil;
}
}

//string以外にも強制変換
public var stringValue:String{
get {
return toString();
}
}

public var array:[AnyObject]?{
get {
if let value: AnyObject = _jsondata {
switch(value) {
case let array as [AnyObject]:
return array as [AnyObject]?
default:
return nil
}
}
return nil;
}
}

public var dictionary:[String : AnyObject]?{
get {
if let value: AnyObject = _jsondata {
switch(value) {
case let value as [String : AnyObject]:
return value as [String : AnyObject]?
default:
return nil
}
}
return nil;
}
}
}


データの比較

Equatableを準拠させて、 == の実装を行う。

extension JSONObject:Equatable {

}

public func ==(lhs:JSONObject,rhs:JSONObject) -> Bool {
//keyの並びなどを考えると、厳密性なし
return lhs.stringValue == rhs.stringValue
}

色々と検討が中途半端ですが、この辺で

とりあえず、SwiftyJSONを使うのが手っ取り早そうですね!