33262


iPhoneアプリを作り始めたのですが、JSONのパースがうまくいかずに非常に困っています。  
JSONは下記のような並びになります。

[code]

    {
    "status":"success",
    "comments":[
     [
      {
       "comments_id":"12",
       "comments_is": true
      }
     ],
     [
      {
       "comments_id":"12",
       "comments_is": true
      }
     ]
    ],
    "documents":[
     {
      "documents_id":"22",
      "documents_date":"221",
      "share": ["22","19"]
     },
     {
      "documents_id":"22",
      "documents_date":"221",
      "share": ["22","19"]
     }
     {
      "documents_id":"22",
      "documents_date":"221"
     }
    ],
    "likes":[
      [
       {
        "liked_id":"42",
        "liked_user":"jj"
       }
      ],
      [
       {
        "liked_id":"42",
        "liked_user":"jj"
       }
      ],
      [
       {
        "liked_id":"42",
        "liked_user":"jj"
       }
      ]
    ]
    }
    
[/code]

ネットで調べると主に下記のようなコードが載っているのを見かけましたので試してみましたがなかなかうまくいかず、catchに入ってしまいます。

[code]

    do {
    
       let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String:AnyObject]
        print(json?["documents"] ?? "none")
    
    
       } catch {
           print ("json error")
           return
    }
    
[/code]

ちなみにJSONの並びを「status」や「comments」だけにした場合ですと、下記のコードで取得できました。

[code]

    do {
    
       let json = try JSONSerialization.jsonObject(with: data, options: []) as? Dictionary<String, AnyObject>
        print(json?["status"] ?? "none")
    
    
       } catch {
           print ("json error")
           return
    }
    
[/code]

「documents」は特に全く所得がうまくいきません。  
お詳しい方いましたらどうぞよろしくお願いします。

Swift3  
xcode 8.2.1

※コード追記しました  
上記のJSONコードが手書きで書いたため、正しく文になっていなかったので、実際のJSON文字列をほぼコピペして記載しました。  
下記が正しいJSONになります。

[code]

    {
      "status": "success",
      "comments": [
        [
    
        ],
        [
          {
            "comments_id": "12",
            "comments_documents_id": "434",
            "comments_user_id": "68",
            "comments_comment": "また",
            "comments_created_at": "2017-01-02 12:59:38",
            "comments_user_name_id": "kur",
            "comment_count": "1"
          }
        ],
        [
    
        ],
        [
    
        ],
        [
    
        ],
        [
    
        ],
        [
          {
            "comments_id": "9",
            "comments_documents_id": "429",
            "comments_user_id": "69",
            "comments_comment": "いいね！",
            "comments_created_at": "2016-12-30 16:34:31",
            "comments_user_name_id": "ho",
            "comment_count": "3"
          },
          {
            "comments_id": "10",
            "comments_documents_id": "429",
            "comments_user_id": "67",
            "comments_comment": "ありがとうございます！",
            "comments_created_at": "2016-12-30 16:35:18",
            "comments_user_name_id": "ne",
            "comment_count": "3"
          },
          {
            "comments_id": "11",
            "comments_documents_id": "429",
            "comments_user_id": "67",
            "comments_comment": "こす",
            "comments_created_at": "2016-12-30 16:36:37",
            "comments_user_name_id": "ne",
            "comment_count": "3"
          }
        ]
      ],
      "documents": [
        {
          "documents_id": "449",
          "documents_user_id": "68",
          "documents_post_type": "1",
          "documents_photo_path": "",
          "documents_message": "たか",
          "documents_private_flag": "0",
          "documents_fence_id": "128",
          "documents_timer": "0",
          "documents_created_at": "20170118220505",
          "documents_distance": 1,
          "follow": "true",
          "user_id": "68",
          "user_nickname": "く",
          "user_name_id": "ku",
          "user_icon_path": "user_icon_img.png",
          "user_official": "0",
          "user_account_private": "false",
          "action_user_nickname": "",
          "action_user_name_id": "",
          "action_user_id": "",
          "action_target_user_id": "",
          "action_created_at": "20170118220505",
          "action": ""
        },
        {
          "documents_id": "434",
          "documents_user_id": "69",
          "documents_post_type": "314",
          "documents_photo_path": "6920170102124729.jpg",
          "documents_message": "またあ",
          "documents_private_flag": "0",
          "documents_fence_id": "112",
          "documents_timer": "0",
          "documents_created_at": "20170102124732",
          "documents_distance": 1,
          "follow": "true",
          "user_id": "69",
          "user_nickname": "ひ",
          "user_name_id": "hi",
          "user_icon_path": "user_icon_img.png",
          "user_official": "0",
          "user_account_private": "false",
          "action_user_nickname": "",
          "action_user_name_id": "",
          "action_user_id": "",
          "action_target_user_id": "",
          "action_created_at": "20170102124732",
          "action": ""
        },
        {
          "documents_id": "433",
          "documents_user_id": "67",
          "documents_post_type": "2",
          "documents_photo_path": "6720161230173125.jpg",
          "documents_message": "たかしまや",
          "documents_private_flag": "0",
          "documents_fence_id": "111",
          "documents_timer": "0",
          "documents_created_at": "20161230173126",
          "documents_distance": 1,
          "follow": "me",
          "user_id": "67",
          "user_nickname": "ねこ",
          "user_name_id": "neko",
          "user_icon_path": "user_icon_img.png",
          "user_official": "1",
          "user_account_private": "true",
          "action_user_nickname": "",
          "action_user_name_id": "",
          "action_user_id": "",
          "action_target_user_id": "",
          "action_created_at": "20161230173126",
          "action": ""
        },
        {
          "documents_id": "432",
          "documents_user_id": "67",
          "documents_post_type": "2",
          "documents_photo_path": "6720161230173048.jpg",
          "documents_message": "こやて",
          "documents_private_flag": "0",
          "documents_fence_id": "111",
          "documents_timer": "0",
          "documents_created_at": "20161230173049",
          "documents_distance": 1,
          "follow": "me",
          "user_id": "67",
          "user_nickname": "ねこ",
          "user_name_id": "neko",
          "user_icon_path": "user_icon_img.png",
          "user_official": "1",
          "user_account_private": "true",
          "action_user_nickname": "",
          "action_user_name_id": "",
          "action_user_id": "",
          "action_target_user_id": "",
          "action_created_at": "20161230173049",
          "action": ""
        },
        {
          "documents_id": "431",
          "documents_user_id": "67",
          "documents_post_type": "2",
          "documents_photo_path": "6720161230173021.jpg",
          "documents_message": "黒",
          "documents_private_flag": "1",
          "documents_fence_id": "111",
          "documents_timer": "0",
          "documents_created_at": "20161230173022",
          "documents_distance": 1,
          "follow": "me",
          "user_id": "67",
          "user_nickname": "ねこ",
          "user_name_id": "neko",
          "user_icon_path": "user_icon_img.png",
          "user_official": "1",
          "user_account_private": "true",
          "action_user_nickname": "",
          "action_user_name_id": "",
          "action_user_id": "",
          "action_target_user_id": "",
          "action_created_at": "20161230173022",
          "action": "",
          "share_user": [
            "72",
            "68"
          ]
        },
        {
          "documents_id": "430",
          "documents_user_id": "69",
          "documents_post_type": "2",
          "documents_photo_path": "6920161230163950.jpg",
          "documents_message": "ビット",
          "documents_private_flag": "0",
          "documents_fence_id": "112",
          "documents_timer": "0",
          "documents_created_at": "20161230163950",
          "documents_distance": 1,
          "follow": "true",
          "user_id": "69",
          "user_nickname": "ひ",
          "user_name_id": "hi",
          "user_icon_path": "user_icon_img.png",
          "user_official": "0",
          "user_account_private": "false",
          "action_user_nickname": "",
          "action_user_name_id": "",
          "action_user_id": "",
          "action_target_user_id": "",
          "action_created_at": "20161230163950",
          "action": ""
        },
        {
          "documents_id": "429",
          "documents_user_id": "67",
          "documents_post_type": "2",
          "documents_photo_path": "6720161230162438.jpg",
          "documents_message": "てすと",
          "documents_private_flag": "0",
          "documents_fence_id": "111",
          "documents_timer": "0",
          "documents_created_at": "20161230162438",
          "documents_distance": 2,
          "follow": "me",
          "user_id": "67",
          "user_nickname": "ねこ",
          "user_name_id": "ne",
          "user_icon_path": "user_icon_img.png",
          "user_official": "1",
          "user_account_private": "true",
          "action_user_nickname": "",
          "action_user_name_id": "",
          "action_user_id": "",
          "action_target_user_id": "",
          "action_created_at": "20161230162438",
          "action": ""
        }
      ],
      "likes": [
        [
    
        ],
        [
          {
            "likes_id": "42",
            "likes_documents_id": "434",
            "likes_user_id": "68",
            "likes_created_at": "2017-01-02 12:59:43",
            "likes_user_name_id": "kuri",
            "likes_count": "1"
          }
        ],
        [
          {
            "likes_id": "45",
            "likes_documents_id": "433",
            "likes_user_id": "77",
            "likes_created_at": "2017-02-15 16:24:34",
            "likes_user_name_id": "kuro",
            "likes_count": "1"
          }
        ],
        [
          {
            "likes_id": "46",
            "likes_documents_id": "432",
            "likes_user_id": "77",
            "likes_created_at": "2017-03-09 18:35:18",
            "likes_user_name_id": "kuro",
            "likes_count": "1"
          }
        ],
        [
    
        ],
        [
    
        ],
        [
          {
            "likes_id": "40",
            "likes_documents_id": "429",
            "likes_user_id": "67",
            "likes_created_at": "2016-12-30 16:56:19",
            "likes_user_name_id": "neko",
            "likes_count": "1"
          }
        ]
      ],
      "share": ""
    }
    
[/code]

※もう少し全体のコードを追記しました。

[code]

    func getPostListsFromMainTimeline() {
        // 通信用のConfigを生成.
        let config:URLSessionConfiguration = URLSessionConfiguration.background(withIdentifier: "backgroundTask")
        // Sessionを生成.
        let session: URLSession = URLSession(configuration: config, delegate: self, delegateQueue: nil)
        // 通信先のURLを生成.
        let url: NSURL = NSURL(string: "http://xxx.xx.xxx.xx/cl/list.php")!
    
        // POST用のリクエストを生成.
        let request: NSMutableURLRequest = NSMutableURLRequest(url: url as URL)
        // POSTのメソッドを指定.
        request.httpMethod = "POST"
    
        // 送信するデータを生成・リクエストにセット.
        let str: NSString = "NAME_ID=\("ne")&TOKEN_KEY=\("1309134774583ed")&TIMELINE=\(currentTimeline)" as NSString
        let myData: NSData = str.data(using: String.Encoding.utf8.rawValue)! as NSData
        request.httpBody = myData as Data
    
        // タスクの生成.
        let task: URLSessionDataTask = session.dataTask(with: request as URLRequest)
        // タスクの実行.
        task.resume()
    }
    
    
    public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
        do {
            let json = try JSONSerialization.jsonObject(with: data, options: []) as? Dictionary<String, AnyObject>
            print(json?["documents"] ?? "ddd")
    
        } catch {
            print ("json error")
            return
        }
    }
    
[/code]


CC BY-SA 3.0
2017-03-10T14:28:10.693
33251
2017-03-11T14:08:39.337
2017-03-11T13:33:33.770
5519
16955
question
swift swift3
JSONのパースに失敗する


あなたのコードは`URLSessionDataDelegate`というのを使って、受信したデータを少しずつ受け取るように構成されています。これは、JSONデータを`JSONSerialization`クラスで処理する場合のようにすべてのデータを受信してからでないと意味がない処理を行うのには向いていません。

具体的には`delegate`を使用することが前提のメソッド `dataTask(with request: URLRequest)`を呼んでいるこの行:

[code]

    let task: URLSessionDataTask = session.dataTask(with: request as URLRequest)
    
[/code]

を、完了ハンドラーで全データ受信後の処理を指定できるこのメソッド `dataTask(with request: URLRequest,
completionHandler: @escaping (Data?, URLResponse?, Error?) ->
Void)`を呼び出すように書き換えてやります:

[code]

    let task = session.dataTask(with: request) {data, response, error in
        //###このクロージャーの中にデータをすべて受信した後の処理を記述する 
        //...
    }
    
[/code]

(`completionHandler`と言うパラメータラベルが見えないのは、Swift特有のtrailing
closureと言う書き方をしているためです。また他の箇所にいくつかの修正をしてもらう前提です。)

* * *

上記のコードを含んだ形で、あなたのコード全体をお勧め修正を含めて書き換えるとこんな感じになります。

[code]

    func getPostListsFromMainTimeline() {
        // 通信用のConfigを生成.###⇒不要
    
        // Sessionを生成.###⇒生成せずに`shared`を使える
        let session = URLSession.shared
        // 通信先のURLを生成.###⇒Swift3では`NSURL`ではなく、可能な限り`URL`を使った方が良い
        //###Swiftが絶対に型推論を間違えない場所では、Swiftに任せた方が良い
        let url = URL(string: "http://xxx.xx.xxx.xx/cl/list.php")!
    
        // POST用のリクエストを生成.###⇒これも`NSMutableURLRequest`ではなく`URLRequest`を使った方が良い
        //###`URLRequest`は値型なので、あとで変更するために`let`ではなく`var`を使用する
        var request = URLRequest(url: url)
        // POSTのメソッドを指定.
        request.httpMethod = "POST"
    
        // 送信するデータを生成・リクエストにセット.###⇒同じく`NSString`ではなく`String`を使った方が良い
        let str = "NAME_ID=\("ne")&TOKEN_KEY=\("xxxxxxxxxx")&TIMELINE=\(currentTimeline)"
        //###さらに同じく`NSData`ではなく`Data`を使った方が良い
        let myData = str.data(using: .utf8)!
        request.httpBody = myData
    
        // タスクの生成.###(1)⇒中間経過を受け取る必要はないので、`completionHandler`を受け付ける方のメソッドを使用
        let task = session.dataTask(with: request) {data, response, error in
            //###このクロージャーの中にデータをすべて受信した後の処理を記述する
            //###元コードの構成と似せるため、ここでは自前のメソッドを呼んでいるが、クロージャー内に直接書いても良い
            self.didReceiveDataResponse(data, response, error)
        }
        // タスクの実行.
        task.resume()
    }
    
    //###データをすべて受信した後の処理
    //###delegateのメソッドではないので、メソッド名はご自由に
    private func didReceiveDataResponse(_ data: Data?, _ response: URLResponse?, _ error: Error?) {
        //###自分でエラーを見つけた時用
        enum MyError: Error {
            case noData
            case nonDictionary
        }
        do {
            //###通信には何らかのエラーがつきもの、開発の初期からきっちりとエラーを拾った方が結局は早道
            if let error = error {throw error}
            guard let data = data
                else {throw MyError.noData}
            guard let json = try JSONSerialization.jsonObject(with: data) as? [String: Any]
                else {throw MyError.nonDictionary}
            print(json["documents"] ?? "none")
        } catch {
            print ("json error: \(error)")
            return
        }
    }
    
[/code]

このコードを参考にしていただければ、正しいJSONデータを受け取れば、正しくパースできるはずです。お試しください。


CC BY-SA 3.0
2017-03-11T13:56:48.443
33262
2017-03-11T14:08:39.337
2017-03-11T14:08:39.337
13972
13972
33251
answer