Swift用Jsonパーサ作った

iOSのなんかを勉強がてら作ろうかなと思ってたらJsonが微妙だったので、これも勉強がてら一緒に作ってしまおうと公開しています。

iOSにはNSJSONSerializationというのがあるのですが、どうも微妙でサードパーティのSwiftyJSONを使うのが普通みたいですが、そこはSwiftの勉強と言う事で。

プロジェクト

githubでMITライセンスで公開しています。

とりあえず作ったという感じで不具合とか使い勝手の悪さとかはあるだろうと思います。

改善点あればPullRequestないしTwitterへのメンションなりで受け付けています。

導入

ほんとは.frameworkにしておきたかったのですが、小一時間調べてもよくわからなかったので、現状はコピペで組み込んでください。

  1. ソース一式をDL
  2. 中にあるSwiftJsonParserから不要なinfo.plistとSwiftJsonParser.hを削除
  3. 組み込みたいプロジェクトをXCodeで開く
  4. SwiftJsonParserフォルダをXCodeのプロジェクトビューのメインターゲットにDnDする
  5. copy if neededとCreate groupにチェックしてインポート

これでおそらく組み込めています。

もし、.frameworkで出せるならビルドしてもらった方が、名前空間も使えて良いと思います。

クラスたち

  • JsonParser
    • Json文字列をJsonオブジェクトへ変換する本体
  • JsonValue
    • Jsonの文字列・数値・真偽値・NULL・配列・オブジェクトをラップした各クラスの基底

Jsonの型オブジェクト

  • StringValue
    • 文字列型のラッパー
  • NumberValue
    • 数値型のラッパー
  • BooleanValue
    • 真偽値型のラッパー
  • NullValue
    • NULLのラッパー
  • ArrayValue
    • 配列のラッパー
  • ObjectValue
    • オブジェクト(連想配列)のラッパー

使い方

パース

JsonParserクラスを利用してJson文字列をオブジェクトへ変換します。

let jsonText = JSON文字列

let parser = JsonParser()
parser.setJsonText(jsonText)

let result = parser.parse()

resultにはJsonValue型が返却されます。

値の取得

値の取得にはJsonValueのasXxxx()メソッドを使います。このメソッドで各Json型として値を解釈します。

(厳密に元のJson文字列のとおりにする必要はなく、互換性がある型なら自動で変換します。 ⇒ 変換対応表)

// {"key1": "value1", "key2": 2.0}
let jsonText = "{\"key1\": \"value1\", \"key2\": 2.0}"
parser.setJsonText(jsonText)
let result = parser.parse()

// ルートをオブジェクトとして取得
let root = result.asObject()

// key1の値を文字列として取得
leu value1 = root.at(key: "key1").asString()
// 中の文字列をアンラップ ⇒ print value1
print(value1.string())

// key2の値を数値として取得
let value2 = root.at(key: "key2").asNumber()
// 中の数値をアンラップ ⇒ print 2.0
print(value2.number().description)

オブジェクトをパースして中の値を取得する例です。

次に、自動型変換の例です。

// {"key1": "3.0", "key2": 2.0}  => 3.0は文字列型
let jsonText = "{\"key1\": \"3.0\", \"key2\": 2.0}"
parser.setJsonText(jsonText)
let result = parser.parse()

// ルートをオブジェクトとして取得
let root = result.asObject()

// key1の値を文字列だが、数値として取得
leu value1 = root.at(key: "key1").asNumber()
// 中の数値をアンラップ ⇒ print 3.0
print(value1.number())

asXxxx()メソッドは可能であれば指定の型として評価を行い自動で変換をします。

例ではkey1の値は文字列型であるため本来はNumberで表現できませんが、自動でDouble(“3.0”)を行いNumberオブジェクトとして返却します。

 

次に、配列がルートの場合です。

// ["value1", "value2", 1.0, 3.0, false]
let jsonText = "[\"value1\", \"value2\", 1.0, 3.0, false]"
parser.setJsonText(jsonText)
let result = parser.parse()

// ルートを配列として取得
let root = result.asArray()

// "value1"
let value1 = root.at(index: 0).asString()
print(value1.string())

// "value2"
let value2 = root.at(index: 1).asString()
print(value2.string())

// 1.0
let value3 = root.at(index: 2).asNumber()
print(value3.number().description)

// 3.0
let value4 = root.at(index: 3).asNumber()
print(value4.number().description)

// false
let value5 = root.at(index: 4).asBoolean()
print(value5.boolean().description)

エラーハンドル

エラー発生時はasXxxx()メソッドがErrorValue型を返すようになります。

例外や停止は発生しません。(不具合が無ければ)

// {"key1": "1.0.1"}
let jsonText = "{\"key1\": \"1.0.1\"}"
parser.setJsonText(jsonText)
let root = parser.parse().asObject()

let value1 = root.at(key: "key1").asNumber()
// true ⇒ 数値として解釈できない物をasNumberした
print(value1.wasErrorOccured())

let value2 = value1.asString()
// true ⇒ 一度エラーを発生させたオブジェクトをずっとエラー
print(value2.wasErrorOccured())

最後に

上にも書きましたが、PullRequestやTwitterへのメンションはご自由に。

あと、.frameworkのちゃんとした作り方もご存じの方がいたら教えてもらえると……

タイトルとURLをコピーしました