iosjsonswiftweb-servicesswift-class

Invalid type in JSON write error trying to send custom class via JSON to .NET web service


I am trying to send data to a .NET web service that accepts three parameters whose types are (String, String, QBUser Class). In Javascript, I would send the parameters in JSON like so:

{sKey: "key", sUpdatetype: "edit", oQbUser: QBUser} 

I have never had any issue with that method before and .NET would be able to sort it right into a class object on the server. With Swift, I have achieved success sending almost every type in the book so far as JSON but I am having issues sending custom classes/struct as a parameter. Here is my current code:

func updateQbUser(k: String, t: String, oUser: QBUUser, completion: @escaping (_ oVisits: AnyObject) -> ()) {
    let params: [String: Any] = ["sKey": k, "sUpdateType": t, "iObjectId": oUser]
    let url = URL(string: "myWebService.asmx/updateQbUser")!
    let session = URLSession.shared
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    do { request.httpBody = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted) } catch let error { print(error.localizedDescription) }
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    request.addValue("application/json", forHTTPHeaderField: "Accept")
    let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
        guard error == nil else { return }
        DispatchQueue.main.async { completion(response!) }
    })
    task.resume()
}

The error as shown in the console is as follows:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Invalid type in JSON write (QBUUser)'

The QBUUser class is not an instance of NSString, NSNumber, NSArray, NSDictionary, or NSNull so I am stuck on how to get this across to the server.

Is there a way to convert the QBUUser class to Any Object type listed above before attempting to send? I tried to run a loop over the class and that was a whole different bag of hammers but got some error about could not AnyHashable over Array Literal or some such. Is there a better way I have not discovered?

The parameters have to be in that way so I am stuck there. I just need to get the class sent over to the other side as what is essentially just in a key/value pair.

here is what the class looks like printed in the console:

  [QBUUser]:
  ID:20786
  created at:2017-05-03 15:05:56 +0000
  updated at:2017-05-03 20:16:52 +0000
  externalUserID:2456369827
  blobID:0
  facebookID:(null)
  twitterID:(null)
  twitterDigitsID:(null)
  full name:Erik Grosskurth
  email:erik@webgraphicsatlanta.com
  login:Erik_2456369827
  phone:(null)
  tags:(
    ""
)
  lastRequestAt:2017-05-03 20:20:16 +0000
  customData:(null)
  website:(null)

Solution

  • You, most likely, just need to convert your QBUUser object into a dictionary. You could do it inline, though if you're planning to reuse that functionality you may as well make a helper function to do it.

    The dictionary should just look something like this:

    let qbuUser = QBUUser() //presumably you already have a reference to the user in question
    let user: [String : Any] = ["id" : qbuUser.ID, "createdAt" : qbuUser.createdAt, "updatedAt" : qbuUser.updatedAt, ...]
    

    If you want to create a helper method in your QBUUser class you could do something like:

    extension QBUUser {
       func toDictionary() -> [String : Any] {
          return ["id", self.ID, "createdAt", self.createdAt, ...]
       }
    }
    

    And that'd allow you to change your existing function to look like this:

    let params: [String: Any] = ["sKey": k, "sUpdateType": t, "iObjectId": oUser.toDictionary()]
    

    It also might be worth pointing out that the key for that QBUUser object in your parameters is "iObjectId", which (to me) implies that all they need is the ID from your user, which makes this entire solution moot.