Moya: nil value in JSON POST body

Created on 11 Mar 2017  ·  10Comments  ·  Source: Moya/Moya

Version 8.0.2

I feel kinda stupid to be asking the seemingly simple question, but I simply can't figure it out.

I need to post some JSON to an API endpoint, where some values need to be nil. Here's an example:

{
    "key1": "someString",
    "key2": null
}

However, the parameters property of the TargetType protocol only accepts [String, Any]? and not [String, Any?]?, so how do you achieve this?

question

Most helpful comment

Leaving this here as reference in case others would find it useful. I was able to use the comments on this issue to help me with my problem of sending the value nil as a parameter value in my network request body.

Moya version: 12.0.1
Swift version: 4.2

struct Contact {
    let name: String 
    let email: String?
} 

/**
 API requires body of JSON to be in format: 
  {
    "contacts": [
        {"name": "Name Here", "email": nil},
        {"name": "Other Name", "email": "[email protected]"}
    ]
  }

It requires nil to be used if the email field does not exist locally.
*/

let contacts: [Contact] = getContacts()

var contactsRequest = [
    [String: Any]
]()
for contact in contacts {
    contactsRequest.append([
        "name": contact.name,
        "email": (contact.email ?? NSNull()) as Any
    ])
}

let parameters = ["contacts": contactsRequest]

// Use `parameters` in `TargetType` for Moya request body. 

After checking the my Moya network log, the request sent to the API is indeed:

{
    "contacts": [
        {"name": "Name Here", "email": nil},
        {"name": "Other Name", "email": "[email protected]"}
    ]
}

Where nil is sent in the request for the "email" parameter.

All 10 comments

Use NSNull() to fill that entry in your dictionary.

Thanks @vzsg. But I'm curious as to why you need to use some old NSObject subclass in a Swift dictionary, when we have optionals?

Also, what is the easiest way to unwrap the optional values when constructing the parameters dictionary then? Let's say I have an object

struct LineupPlayer {
    let id: Int
    let user : User?
}

doing the following

extension LineupPlayer : ParameterRepresentation {
    var representation: [String : Any] {
        return ["id": id, "user": user?.representation ?? NSNull()]
    }

results in a compile error Generic parameter 'T' could not be inferred, since the nil-coalescing operator tries to operate on two different types.

How would you recommend to do this?

To provide a default value of an optional of [String: Any], you could use an empty dictionary [:] instead of NSNull.

On 12 Mar 2017, 4:27 PM +0800, Philip Engberg notifications@github.com, wrote:
>

Thanks @vzsg (https://github.com/vzsg). But I'm curious as to why you need to use some old NSObject subclass in a Swift dictionary, when we have optionals?

Also, what is the easiest way of constructing the parameters dictionary then? Let's say I have an object

struct LineupPlayer { let id: Int let user : _User? }

doing the following

extension LineupPlayer : ParameterRepresentation { var representation: [String : Any] { return ["id": id, "user": user?.representation ?? NSNull()] }

results in a compile error Generic parameter 'T' could not be inferred, since the nil-coalescing operator tries to operate on two different types.

How would you recommend to do this?


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub (https://github.com/Moya/Moya/issues/1007#issuecomment-285930025), or mute the thread (https://github.com/notifications/unsubscribe-auth/AAEG8NlI1Fm4caHS0IJX5UTbZzpr285kks5rk6yLgaJpZM4MaS-O).

Doing

extension LineupPlayer : ParameterRepresentation {
    var representation: [String : Any] {
        return ["id": id, "user": (user?.representation as? Any`) ?? NSNull()]
    }

compiles, but now I get this error 🙄 I'm so confused!

Why don't you do it as the following? Also, what is the type of representation? Is it the same as LineupPlayer representation?

/// What is the expected type of user?.representation

extension LineupPlayer : ParameterRepresentation {
    var representation: [String : Any] {
        return ["id": id, "user": user?.representation as? [String: Any] ?? [:]]
    }
}

It's just a protocol

protocol ParameterRepresentation {
    var representation : [String : Any] { get }
}

Because the API only accepts null, not { }, when there is no value @jessearmand.

Alright, understood. It compiles, but there's an error? or a warning?

If it's a warning I don't think that is an issue since that is what you're doing, coercing [String: Any] to Any and using NSNull as a default, which is a different type from [String: Any].

I got it working now. Thanks everyone 🙏

Great! Could you post your fix to the problem here as well, @philipengberg? Might be helpful for other people in the future.

Leaving this here as reference in case others would find it useful. I was able to use the comments on this issue to help me with my problem of sending the value nil as a parameter value in my network request body.

Moya version: 12.0.1
Swift version: 4.2

struct Contact {
    let name: String 
    let email: String?
} 

/**
 API requires body of JSON to be in format: 
  {
    "contacts": [
        {"name": "Name Here", "email": nil},
        {"name": "Other Name", "email": "[email protected]"}
    ]
  }

It requires nil to be used if the email field does not exist locally.
*/

let contacts: [Contact] = getContacts()

var contactsRequest = [
    [String: Any]
]()
for contact in contacts {
    contactsRequest.append([
        "name": contact.name,
        "email": (contact.email ?? NSNull()) as Any
    ])
}

let parameters = ["contacts": contactsRequest]

// Use `parameters` in `TargetType` for Moya request body. 

After checking the my Moya network log, the request sent to the API is indeed:

{
    "contacts": [
        {"name": "Name Here", "email": nil},
        {"name": "Other Name", "email": "[email protected]"}
    ]
}

Where nil is sent in the request for the "email" parameter.

Was this page helpful?
0 / 5 - 0 ratings