28216


swift3.0でJSONを解析したいのですが下記のエラーが出てしまい困っています。  
swift、PHPともに勉強中ですのでまだ理解してない部分も多いです。  
このような説明で大変申し訳ないのですが、ご教示お願いいたします。

> Error 43-> Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not
> start with array or object and option to allow fragments not set."
> UserInfo={NSDebugDescription=JSON text did not start with array or object
> and option to allow fragments not set.}

以下ソースコードになります。  
.swift

[code]

    let array = ["data=文字列"]
    
        do {
            let jsonData = try JSONSerialization.data(withJSONObject: array, options: .prettyPrinted)
    
            let request = NSMutableURLRequest(url: NSURL(string: "http://localhost/test.php")! as URL)
            request.httpMethod = "POST"
            request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
            request.httpBody = jsonData
            request.addValue("application/json", forHTTPHeaderField: "Content-Type")
            request.addValue("application/json", forHTTPHeaderField: "Accept")
    
            let task = URLSession.shared.dataTask(with: request as URLRequest){ data, response, error in
                if error != nil{
                    print("Error 55 -> \(error)")
                    return
                }
                do {
                    let result = try JSONSerialization.jsonObject(with: data!)
                    print("Result 34 -> \(result)")
                } catch {
                    print("Error  43-> \(error)")
                }
            }
            task.resume()
        }
        catch {
            print(error)
        }
    
[/code]

.php

[code]

    $json = $_POST['data'];
    header('Content-type: application/json');
    echo json_encode($json);
    
[/code]


CC BY-SA 3.0
2016-08-11T20:27:58.727
28206
2016-08-12T10:00:20.013
16882
question
swift php json swift3
Domain=NSCocoaErrorDomain Code=3840について


Swift 側と PHP 側のロジックが噛み合っていないです。

Swift 側では、リクエストとして `["data=文字列"]` という JSON を送信しています。

一方、PHP 側では、`$_POST['data']` を受け取ろうとしていますが、こちらはフォームの `data`
要素を取得するものです。ですので、これは必ず空値になります。

  * `$_POST`変数には何が格納されるのか？
  * PHP で JSON 本文を受け取るにはどうすればいいのか？

というところを調べてみてください。

* * *

Swift 側だけで対応しようとすると、こんな感じです。

[code]

    let data = "文字列"
    
    guard let url = URL(string: "http://localhost/test.php") else {
       return
    }
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
    request.httpBody = "data=\"\(data)\"".data(using: .utf8)
    request.addValue("application/json", forHTTPHeaderField: "Accept")
    
    let task = URLSession.shared().dataTask(with: request){ data, response, error in
    
       if let error = error {
           print("Error 55 -> \(error)")
           return
       }
    
       if let data = data {
           do {
               let result = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
               print("Result 34 -> \(result)")
           } catch {
               print("Error  43-> \(error)")
           }
       }           
    }
    task.resume()
    
[/code]

いくつか注意点です。

  * `"[data=文字列]"` を JSON にした場合、 `data=文字列` という文字列を要素に持つ、単一の配列になります。おそらく意図しているところは、`{data:"文字列"}` という連想配列ではないでしょうか。
  * Swift3 では、`NSURL` クラスではなく、`URL` 構造体を使うことが望ましいです。不要な型変換がなくなります。
  * 同じく、`NSMutableURLRequest` クラスではなく、 `URLRequest` 構造体を使ってください。`Mutable` であることを示すために、`var` で宣言します。
  * PHP 側の仕様に合わせて、フォームとして値を送信する処理に変更しています。
  * サーバーから返却される JSON のルート要素は、「文字列」という `String` になりますが、`JSONSerialization` はデフォルトではルート要素が配列もしくはオブジェクトしか受け付けません。もしルート要素が `String` となるような JSON を受信したい場合には、`.allowFragments` のオプションが必要になります。


CC BY-SA 3.0
2016-08-12T05:18:53.917
28207
2016-08-12T05:39:47.933
2016-08-12T05:39:47.933
5337
5337
28206
answer


quesera2 さんのご回答にあるように「Swift 側と PHP
側のロジックが噛み合っていない」のが問題ですので、本来はご質問者にどの部分が必須でどの部分は必須でないのかをきちんと明らかにしていただいてから答えるのが筋かと思うのですが、一つの想定としてこんな感じになりますと言うのを例としてあげておきます。

  * 送信データは"application/json"として送信したい
  * サーバ側では(動作確認用に)受信したデータを丸ごと返したいだけ
  * サーバ側ではJSON内の'data'要素を(必要であれば)とり出せるようにしておきたいが今は不要

と言うわけで、まずは **Swift側** から:

[code]

        let jsonObject = ["data": "文字列"]
    
        do {
            let jsonData = try JSONSerialization.data(withJSONObject: jsonObject, options: .prettyPrinted)
    
            var request = URLRequest(url: URL(string: "http://localhost/test.php")!)
            request.httpMethod = "POST"
            request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
            request.httpBody = jsonData
            request.addValue("application/json", forHTTPHeaderField: "Accept")
    
            let task = URLSession.shared.dataTask(with: request){ data, response, error in
                if let error = error {
                    print("Error 55 -> \(error)")
                    return
                }
                do {
                    let result = try JSONSerialization.jsonObject(with: data!)
                    print("Result 34 -> \(result)")
                } catch {
                    print("Error  43-> \(error)")
                }
            }
            task.resume()
        } catch {
            print(error)
        }
    
[/code]

  * 送信するデータを`array = ["data=文字列"]`から`jsonObject = ["data": "文字列"]`と変更してありますが、これはあまり本質的ではありません。

上記の2つめの条件の「サーバ側では(動作確認用に)受信したデータを丸ごと返したいだけ」と言うのがありならば、Swift側では`NSArray`か`NSDictionary`に変換可能なデータであれば良いと言うことになります。ご質問にあるコードでも(下記のPHP側の修正をすれば)正しく送受信できます。

  * 送信時の"Content-Type"の設定は一度で良いので、2つ目は省略

  * NSURLRequest, NSURLの使い方については、quesera2さんの回答の注意点の通りです

続いて **PHP側**

[code]

    $json_string = file_get_contents('php://input');
    $json = json_decode($json_string);
    header('Content-type: application/json');
    echo json_encode($json);
    
[/code]

上記のSwift側の方法で送信されたデータは、HTTPリクエストのボディ部全体にJSONのテキスト表現を表すバイトデータが埋め込まれる形で送信されることになり、PHPが`$_POST`として解釈可能な、"application/x-www-
form-urlencoded"にも"multipart/form-
data"にもなっていません。そのような場合、自前でHTTPリクエストのボディ部を読み込んで、それをJSONとして解釈する必要があります。上記の最初の2行がそれをやるコードで、3行目と4行目はそれをもう一度"application/json"として丸ごと返しているだけです。

一つの回答にするには、あれやこれや盛り込みすぎですので、少々わかりにくいかも知れませんが、最初に書いたような前提を置くとこのようなコードが書けますと言うことでお受け取りください。もし少々の修正でご質問者さんの想定通りの動きをさせられそうであれば、ご質問を修正くださるか、コメント等でお知らせください。


CC BY-SA 3.0
2016-08-12T10:00:20.013
28216
2016-08-12T10:00:20.013
13972
28206
answer