Alamofire: body with an empty json not getting encoded

Created on 28 Sep 2016  路  10Comments  路  Source: Alamofire/Alamofire

Im sending a body that has in it an empty json.

["and": [["id": ["inq":["1", "2", "3"]]], [:]]]

Im using a URLRequestConvertible enum.
after the encoding in asURLRequest
try URLEncoding.default.encode(urlRequest, with: params)

I get

query[and][][id][inq][]=55b6725435c49b0300a1f979
and its missing the empty json.

Is there a way to leave the empty json in the body?

Thanks

support

Most helpful comment

Thanks for posting that @ilandbt...I've found a few issues in your expectations. First off, the JSON you just posted isn't valid. You are encoding arrays as dictionaries which results in invalid JSON. The valid form of the dictionary in your original question is:

import Foundation

let parameters: [String: Any] = ["and": [["id": ["inq":["1", "2", "3"]]], [:]]]

do {
    let data = try JSONSerialization.data(withJSONObject: parameters, options: [])

    if let utf8 = String(data: data, encoding: .utf8) {
        print("JSON: \(utf8)")
    }
} catch {
    print(error)
}

// Outputs: {"and":[{"id":{"inq":["1","2","3"]}},{}]}

This is certainly your core issue "if" the invalid JSON you posted is actually what you need to send. In case you just posted the JSON wrong, I went a step further to make sure the empty JSON dictionary is actually encoded when using JSONEncoding.

func testEmptyJSON() {
    let urlRequest = URLRequest(url: URL(string: "https://httpbin.org/get")!)
    let parameters: Parameters = ["and": [["id": ["inq":["1", "2", "3"]]], [:]]]

    do {
        let httpBody = try JSONEncoding.default.encode(urlRequest, with: parameters).httpBody

        if let httpBody = httpBody, let utf8 = String(data: httpBody, encoding: .utf8) {
            print("JSON: \(utf8)")
        }
    } catch {
        print(error)
    }
}

// Outputs: {"and":[{"id":{"inq":["1","2","3"]}},{}]}

As you can see, it outputs the same JSON as the other example and DOES include the empty dictionary.

What I'd recommend is using the debugPrint(request) approach to print out the cURL command for the request. That will show you exactly what's being sent to the server.

If you are debugging on the server, then I'd try to figure out if the server is stripping off the empty dictionary although I really doubt it. You could also use the cURL command and something like Charles Proxy to continue to debug.

At this point, this is on you to investigate because Alamofire is working exactly as it should in this case. If you do need to encode the invalid JSON that you posted, then you'll need to implement your own ParameterEncoding type because Alamofire does not encode invalid JSON.

If you continue to have issues, please take your question to Stack Overflow.

Cheers. 馃嵒

All 10 comments

You want to use URLEncoding.queryString instead. That will leave your httpBody intact. I'd definitely suggest reading through the docstrings for URLEncoding to get a better feel for the options available to you.

Cheers. 馃嵒

Thanks @cnoon .
This doesn't work.

Using .methodDependent, .httpBody gives me the same result (missing empty json).
Using .queryString appends the body to the query instead of the http body.

Can you give us your brief function code how you are calling and adding perams.?

enum AssignmentRouter: URLRequestConvertible {

    case assign(params:[String:Any])


    var method: Alamofire.HTTPMethod {
        switch self {
        case .assign:
            return .post
        }
    }

    var path: String {
        switch self {

        case .assignForms(params: _):
            return "/**/a"
        }
    }

    var params: [String: Any]? {
        switch self {
        case .assign(let params):
            return params
        }
    }

    func asURLRequest() throws -> URLRequest {
        let url = (APIManager.baseURL + path).encodeUrl()
        var urlRequest = URLRequest(url: URL(string: url!)!)

        urlRequest.httpMethod = method.rawValue

        //add headers
        APIManager.addHeraders(urlRequest: &urlRequest)

        return  try URLEncoding.default.encode(urlRequest, with: params)
    }
}

Please Try this
first convert your parameter in to JSON string and use this

try! StringBodyEncoding(string: JSONString ).encode(urlRequest,with:nil)

Custom Encoding Struct

struct StringBodyEncoding: ParameterEncoding {
    private let string: String

    init(string: String) {
        self.string = string
    }

    func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
        var urlRequest = urlRequest.urlRequest
        urlRequest?.httpBody = string.data(using: String.Encoding.utf8, allowLossyConversion: false)
        return urlRequest!
    }
}

Not working for me, the empty JSON is still missing.

if let data = try? JSONSerialization.data(withJSONObject: params!, options: []), let s = String(data: data, encoding: String.Encoding.utf8)  {

            return try! StringBodyEncoding(string: s).encode(urlRequest, with:nil)

        }

What is the empty JSON you would expect to send @ilandbt...let's start there. What would you like to send up to the server. By empty do you mean nothing? I'm assuming not, but have to ask. I would "assume" you're trying to send an empty dictionary {}. Is that correct?

@cnoon i want to send this JSON:

{"and": {{"id": {"inq":{"1", "2", "3"}}}, {}}}

Thanks for posting that @ilandbt...I've found a few issues in your expectations. First off, the JSON you just posted isn't valid. You are encoding arrays as dictionaries which results in invalid JSON. The valid form of the dictionary in your original question is:

import Foundation

let parameters: [String: Any] = ["and": [["id": ["inq":["1", "2", "3"]]], [:]]]

do {
    let data = try JSONSerialization.data(withJSONObject: parameters, options: [])

    if let utf8 = String(data: data, encoding: .utf8) {
        print("JSON: \(utf8)")
    }
} catch {
    print(error)
}

// Outputs: {"and":[{"id":{"inq":["1","2","3"]}},{}]}

This is certainly your core issue "if" the invalid JSON you posted is actually what you need to send. In case you just posted the JSON wrong, I went a step further to make sure the empty JSON dictionary is actually encoded when using JSONEncoding.

func testEmptyJSON() {
    let urlRequest = URLRequest(url: URL(string: "https://httpbin.org/get")!)
    let parameters: Parameters = ["and": [["id": ["inq":["1", "2", "3"]]], [:]]]

    do {
        let httpBody = try JSONEncoding.default.encode(urlRequest, with: parameters).httpBody

        if let httpBody = httpBody, let utf8 = String(data: httpBody, encoding: .utf8) {
            print("JSON: \(utf8)")
        }
    } catch {
        print(error)
    }
}

// Outputs: {"and":[{"id":{"inq":["1","2","3"]}},{}]}

As you can see, it outputs the same JSON as the other example and DOES include the empty dictionary.

What I'd recommend is using the debugPrint(request) approach to print out the cURL command for the request. That will show you exactly what's being sent to the server.

If you are debugging on the server, then I'd try to figure out if the server is stripping off the empty dictionary although I really doubt it. You could also use the cURL command and something like Charles Proxy to continue to debug.

At this point, this is on you to investigate because Alamofire is working exactly as it should in this case. If you do need to encode the invalid JSON that you posted, then you'll need to implement your own ParameterEncoding type because Alamofire does not encode invalid JSON.

If you continue to have issues, please take your question to Stack Overflow.

Cheers. 馃嵒

Thanks @cnoon
My problem was using URLEncoding instead of 'JSONEncoding'
The invalid JSON was a mistake in the comment.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

yamifr07 picture yamifr07  路  3Comments

Tulleb picture Tulleb  路  3Comments

shivang2902 picture shivang2902  路  3Comments

hengchengfei picture hengchengfei  路  3Comments

sarbogast picture sarbogast  路  3Comments