0

I am sending a POST request to my backend however i am getting this error:

The given data was not valid JSON.",
underlyingError: Optional(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.})))

And this is the code that i am using to send/fetch data:

func fetchDataWithParameters(){

    struct Response: Codable {
        let status: String?
        let error: String?
    }

    let decoder = JSONDecoder()
    HTTP.POST("somelinkhere", parameters: ["date": self.weekDays[self.itemSelectedIndex]]) { response in
        if let error = response.error {
            print("got an error: \(error)")
            return
        }
        do {
            let resp = try decoder.decode(Response.self, from: response.data)
            if let err = resp.error {
                print("got an error: \(err)")
            }
            if let status = resp.status {
                print("completed: \(status)")

            }
        } catch let error {
            print("decode json error: \(error)")
        }
    }
}

Using my terminal i am trying to do a manual POST request and i get this:

Admins-MacBook-Pro:hello-world admin$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" somelinkhere
HTTP/1.1 200 OK
Server: openresty/1.9.15.1
Date: Thu, 03 May 2018 23:42:04 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 39
Connection: keep-alive
X-Clacks-Overhead: GNU Terry Pratchett

"{\"name\": \"Timo\", \"age\": \"39\"}"

This leaves me wondering that the only possible fault can be how i am decoding the JSON. Why else would it work with a terminal? Any ideas?

As @patru suggested i included the print here:

catch let error {
    print(String(data:response.data, encoding: .utf8)!)
    print("decode json error: \(error)")
}

This was the result:

"{\"name\": \"Mergim\", \"age\": \"39\"}"
decode json error: dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(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.})))

It seems as if i am getting the JSON as i did with the curl but for some reason swift does not see it as valid JSON? This is how my post method looks like in the backend:

@app.route('/', methods=['GET', 'POST'])
def index():

    jsonData = {"name": "Timo", "age": "39"}
    jsonData1 = {"name": "Mergim", "age": "39"}

    if request.method=='GET':
        return json.dumps(jsonData)

    elif request.method=='POST':
        return json.dumps(jsonData1)

EDIT

    jsonData = '{"name": "Timo", "age": "39"}'
    jsonData1 = '{"name": "Mergim", "age": "39"}'

changed to:

    jsonData = {"name": "Timo", "age": "39"}
    jsonData1 = {"name": "Mergim", "age": "39"}
EmbeddedOS
  • 173
  • 6
  • 18
  • Given your `curl` output this should be valid JSON, given your error message it seems not to be. Hard to tell without seeing what you _actually_ get. Please try printing `print(String(data:response.data, encoding: .utf8))` and provide us with the output, that will probably help. (And please do it by editing your question, as you can see code tends to look messy in (more limited) comments.) – Patru May 04 '18 at 02:24
  • Where in your `curl` command are you passing the "date" parameter that you are passing in your code? – Mike Taverne May 04 '18 at 03:01
  • @MikeTaverne It does not matter in this case. I am just simply using any value just to get a response. The implementation of date will come later. – EmbeddedOS May 04 '18 at 09:20

1 Answers1

0

This is subtle, but, believe it or not, the problem is on the Server!

Actually your code looked ... a little off, but still quite OK, so I ran this Playground:

import Cocoa

let jsonData = "{\"name\": \"Mergim\", \"age\": \"39\"}".data(using: .utf8)!

struct Response: Codable {
    let status: String?
    let error: String?
}

struct NameAge: Codable {
    let name: String
    let age: String
}

do {
    let resp = try JSONDecoder().decode(Response.self, from: jsonData)
    print(resp)
    let na = try JSONDecoder().decode(NameAge.self, from: jsonData)
    print(na)
} catch {
    print(error)
}

print(String(data:jsonData, encoding: .utf8)!)

Since your Response struct does not quite match the structure of your data I implemented NameAge in order to have something more fitting. Still both do parse, although the value of Response remains questionable. I was puzzled too, until I checked out your error message: it complains about the first character in your data! I finally added the last line in the Playground and the problem became clear.

You provided your server (I guess it is in ruby or something close to it) with a valid String object and it turned it into the corresponding JSON which still represents just a String in JSON. This explains the double quotes which you have and my Playground does not produce. You probably should provide your jsonData as a Hash and the server will convert it properly (without the double quotes). For a random reason JSONDecoder does not want to decode a simple JSON-string, but that is something I excuse it for.

Btw: You probably want to change the type of age to Int once you can pass it correctly.

Patru
  • 4,481
  • 2
  • 32
  • 42
  • Taking what you said into consideration i just changed the way i create my Strings in the server. Check the edit i made on the server code. I get no errors now but why wont it print out "completed", i managed to print the response and it was correct but i want to be able to do stuff when it reaches the completion part of the request which is if let status = resp.status ?? – EmbeddedOS May 06 '18 at 15:29
  • I thought I had made this clear in my code: You will not be able to decode `status` and `error` from a piece of JSON that _only_ contains `name` and `age`. As long as you define them as optionals your `decode` will still work, but your `status` will always be empty. You should be able to get some HTTP-status from the `response` object you are given by the `HTTP.POST` method. Running my Playground you will find that you can _only_ get reasonable values from the given String if you `decode` `NameAge`, _not_ `Response`. – Patru May 07 '18 at 00:03