JSON data in http body contains special characters in my simple case - ios

I have a very simple model struct Student which only has two properties firstName and lastName:
struct Student {
let firstName: String
let lastName: String
init(_ firstName: String, _ lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
// Convert to json data
func toData() -> Data? {
var json = [String: Any]()
let mirror = Mirror(reflecting: self)
for child in mirror.children {
if let key = child.label?.trimmingCharacters(in: .whitespacesAndNewlines) {
json[key] = child.value
}
}
do {
return try JSONSerialization.data(withJSONObject: json, options: [])
} catch {
print("\(error.localizedDescription)")
}
return nil
}
}
As you see above, I created an toData() function which I use to convert model object to JSON data for my HTTP request body.
I create Student instance by:
let student = Student("John", "Smith")
I got Json Data by:
let jsonData = student.toData()
later I set to my URLRequest body by:
request.httpBody = jsonData!
However, backend team always see :
{\"firstName\":\"John\", \"lastName\":\"Smith\"}
But backend expect:
{"firstName":"John", "lastName":"Smith"}
I am sure it is not backend problem. Looks like something needs to improve in my toData() function. But I don't know what to do. Could someone help me?

Try this :
if let jsonString:String = String(data: jsonData!, encoding: String.Encoding.utf8) {
request.httpBody = jsonString.data(using: String.Encoding.utf8)
}

You can get rid of the extra backslashes by converting your struct to a dictionary manually.
I have tested below method using a dummy rest server (rest-server-dummy from npm) and there are no extra backslashes around the "" characters.
struct Student {
let firstName: String
let lastName: String
init(_ firstName: String, _ lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
// Convert to json data
func toData() -> Data? {
var json = [String: Any]()
json["firstName"] = firstName
json["lastname"] = lastName
do {
return try JSONSerialization.data(withJSONObject: json, options: [])
} catch {
print("\(error.localizedDescription)")
}
return nil
}
}
I used this method to send the data to the dummy server running on localhost:
var request = URLRequest(url:URL(string: "http://localhost:8080/username")!)
request.httpMethod = "POST"
request.httpBody = student.toData()
URLSession.shared.dataTask(with: request, completionHandler: { data, response, error in
data
response
error
}).resume()
The contents of the output from the server log:
{
"lastname": "Smith",
"firstName": "John"
}

Related

How to convert stored values to JSON format using Swift?

I am trying to convert stored coredata values to JSON format and the JSON format value need to assign a single variable, because this generated JSON I need to send to server. Below code I tried to get coredata stored values but don’t know how to generate JSON required format.
Getting values from coredata
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "User")
do {
let results = try context.fetch(fetchRequest)
let dateCreated = results as! [Userscore]
for _datecreated in dateCreated {
print("\(_datecreated.id!)-\(_datecreated.name!)") // Output: 79-b \n 80-c \n 78-a
}
} catch let err as NSError {
print(err.debugDescription)
}
Need to Convert Coredata Value to Below JSON format
{
    "status": true,
    "data": [
        {
            "id": "20",
            "name": "a"
        },
        {
            "id": "21",
            "name": "b"
        },
        {
            "id": "22",
            "name": "c"
        }
    ]
}
Probably the easiest is to convert your object(s) to either dictionaries or arrays (depending on what you need).
First you need to be able to convert your Userscore to dictionary. I will use extension on it since I have no idea what your entity looks like:
extension Userscore {
func toDictionary() -> [String: Any]? {
guard let id = id else { return nil }
guard let name = name else { return nil }
return [
"id": id,
"name": name
]
}
}
Now this method can be used to generate an array of your dictionaries simply using let arrayOfUserscores: [[String: Any]] = userscores.compactMap { $0.toDictionary() }.
Or to build up your whole JSON as posted in question:
func generateUserscoreJSON(userscores: [Userscore]) -> Data? {
var payload: [String: Any] = [String: Any]()
payload["status"] = true
payload["data"] = userscores.compactMap { $0.toDictionary() }
return try? JSONSerialization.data(withJSONObject: payload, options: .prettyPrinted)
}
This will now create raw data ready to be sent to server for instance
var request = URLRequest(url: myURL)
request.httpBody = generateUserscoreJSON(userscores: userscores)
You can use the properties of an Encodable to make this happen. This has the added benefit of not resorting to the Any type.
For the JSON, you could use the following types:
struct JSONMessage: Encodable {
var status: Bool
var data: [JSONDataEntry]
}
struct JSONDataEntry: Encodable {
var id: String
var name: String
}
Then you can adjust your do/try/catch as follows:
do {
let results = try context.fetch(fetchRequest)
let dateCreated = results as! [Userscore]
// *starting here*
let data = dateCreated.map { JSONDataEntry(id: String($0.id!), name: $0.name!) }
let status = true // <- not sure where status comes from, so adding here
let message = JSONMessage(status: status, data: data)
let jsonData = try JSONEncoder().encode(message)
if let json = String(data: jsonData, encoding: .utf8) {
// do something with the JSON string
print(json)
}
// *ending here*
} catch let err as NSError {
print(err.debugDescription)
}

Passing JSON result into a struct model

I am receiving a result from an API, I can iterate through the result. My understanding is I can pass the value into a model immediately.
Apple Developer article on struct models
My issue is I am not doing it properly and am receiving a nil value. Perhaps someone can see where I need to change. I am using Swift 4.2
Here is my struct model.
import Foundation
struct ProfileModel {
//MARK: Properties
var name: String
var email: String
var profileURL: String
//MARK: Initialization
}
extension ProfileModel{
init?(json: [String:AnyObject]) {
guard
let name = json["name"] as? String,
let email = json["email"] as? String,
let profileURL = json["profileURL"] as? String
else { return nil }
self.name = name
self.email = email
self.profileURL = profileURL
}
}
Here is my result code from my urlConnection. Let me know if we want to see the entire swift file
//create dataTask using the session object to send data to the server
let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
guard error == nil else {
return
}
guard let data = data else {
return
}
do {
//create json object from data
if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String:AnyObject] {
self.onSuccess(data: json)
}
} catch let error {
print(error.localizedDescription)
}
})
task.resume()
}
func onSuccess(data: [String:AnyObject]){
print("onSuccess")
let myProfile = ProfileModel(json: data)
//myProfile is nil while unwrapping
let title: String = myProfile!.name
print(title)
}
I could just iterate through the strings since I am able to print 'data'. I just figured it would be cleaner to put everything into a ProfileModel and manage that object as a whole.
This json is my more simple one which is why I used it for this question. I also can't remember but I had to use "[String:AnyObject]" to get the json properly. This was pulled directly from my terminal, this was the data being passed in my JsonResponse. The output json from Xcode has [] on the outside instead.
{
'detail': 'VALID',
‘name’: ‘Carson,
'email': ‘carson.skjerdal#somethingelselabs.com',
'pic_url': None
}
EDIT:
So my problem is solved, and ultimately moving to Codable was the key. Here is my fixed code for anyone who might need a working solution.
URLSession.shared.dataTask(with: request as URLRequest) { (data, response
, error) in
guard let data = data else { return }
do {
let decoder = JSONDecoder()
let gitData = try decoder.decode(ProfileModel.self, from: data)
print(gitData.name)
self.onSuccess(data: gitData)
} catch let err {
print("Err", err)
}
}.resume()
}
func onSuccess(data: ProfileModel){
print("onSuccess")
print(data.email)
}
My Codable Struct - slightly simplified
import Foundation
struct ProfileModel: Codable {
let detail, name, email: String
private enum CodingKeys: String, CodingKey {
case detail, email
case name = "firstname"
//case picUrl = "pic_url"
}
}
After "Codable" has been introduced I always uses that.
You can take your JSON ans pars it in to QuickType.io, and you will get a Struct that confirms to the codadable
// To parse the JSON, add this file to your project and do:
//
// let aPIResponse = try? newJSONDecoder().decode(APIResponse.self, from: jsonData)
import Foundation
struct APIResponse: Codable {
let detail, name, email, picUrl: String
enum CodingKeys: String, CodingKey {
case detail, name, email
case picUrl = "pic_url"
}
}

Display lastName and name in application after retrieve value key in token Swift 4

I come back with my problem and I still find no answer
according to the previous solutions I had to develop the function to retrieve information (lastname, name) on the token
func loadMemberProfil(completion: ((_ sub : [String: AnyObject]) -> Void)!) {
// get API profile and Bearer token
let token = HPWSLoginManager.shared().saveSuccessResponse.token
let url = URL(string: "http://51.38.36.76:40/api/v1/profile")
var request = URLRequest(url: url!)
request.httpMethod = "GET"
request.addValue("Bearer \(token!)", forHTTPHeaderField: "Authorization")
//get information in token
URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data else { return }
do {
let json = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as! [String: AnyObject]
let sub = json["sub"] as! [String: AnyObject]
if completion != nil{
completion(sub)
}
} catch {
print("error")
}
}.resume()
}
And in this function (that I call in viewDidLoad()) I want to display the name and lastname but nothing is displayed when I run the emulator
func initTableViewModel() {
self.loadMemberProfil { (sub) in
let headerMenu = HPMenuHeaderViewModel.init(firstName: "\(sub["firstname"])", lastName: "\(sub["lastname"])")
let tableViewModel = HPMenuViewModel(titlePage: "Menu", array: arrayCellModel, headerMenuViewModel: headerMenu)
self.tableViewModel = tableViewModel
}
}
but i recover the values in the console please hep me
You are fetching but not reloading the data. You might want to read the documentation to understand the problem.
After you initTableViewModel you need to use the code below to reload the data. Note that the DispatchQueue.main.async is for doing that in the main thread.
DispatchQueue.main.async {
self.tableView.reloadData()
}
in HPMenuHeaderViewModel class
class HPMenuHeaderViewModel: NSObject {
var imageName: UIImage?
var firstName: String?
var lastName: String?
var numberString: String?
var name: String {
return String.safe(firstName) + " " + String.safe(lastName)
}
var nameInitials: String {
let arrayName = self.name.components(separatedBy: " ")
return arrayName.reduce("") { $0.safePrefix(1) + $1.safePrefix(1)}
}
#objc init(firstName: String, lastName: String, numberString: String) {
super.init()
self.firstName = firstName
self.lastName = lastName
self.numberString = numberString
}
}
in HPMenuViewModel class
class HPMenuViewModel: NSObject {
var titlePage: String?
var accountTableViewCellModel: [HPMenuTableViewCellModel]?
var headerMenuViewModel: HPMenuHeaderViewModel?
init(titlePage: String, array accountTableViewCellModel: [HPMenuTableViewCellModel]?, headerMenuViewModel: HPMenuHeaderViewModel ) {
super.init()
self.titlePage = titlePage
self.accountTableViewCellModel = accountTableViewCellModel
self.headerMenuViewModel = headerMenuViewModel
}
}
I use MVVM architecture

serialization and de-serialization of an object in Swift3

I want to know if there is a possibility of make a serialization and de-serialization of an object in swift 3.
I have an object like this:
class Credentials{
var username:String;
var password:String;
init(){
username = "";
password = "";
}
}
I want to transform this class into a json (and vice versa ) to send it through HTTP post.
I don't want to use Third party libraries.
Thanks for the answer.
First of all it's not necessary to use a class, a struct is sufficient.
Simple solution with an failable initializer expecting a JSON string and a variable jsonRepresentation
struct Credentials {
var username = ""
var password = ""
init(username: String, password:String) {
self.username = username
self.password = password
}
init?(json : String) {
guard let data = json.data(using: .utf8),
let jsonDict = try? JSONSerialization.jsonObject(with: data, options: []) as? [String:String],
let username = jsonDict?["username"],
let password = jsonDict?["password"] else { return nil }
self.username = username
self.password = password
}
var jsonRepresentation : String {
let jsonDict = ["username" : username, "password" : password]
if let data = try? JSONSerialization.data(withJSONObject: jsonDict, options: []),
let jsonString = String(data:data, encoding:.utf8) {
return jsonString
} else { return "" }
}
}

Convert array to JSON string in swift

How do you convert an array to a JSON string in swift?
Basically I have a textfield with a button embedded in it.
When button is pressed, the textfield text is added unto the testArray.
Furthermore, I want to convert this array to a JSON string.
This is what I have tried:
func addButtonPressed() {
if goalsTextField.text == "" {
// Do nothing
} else {
testArray.append(goalsTextField.text)
goalsTableView.reloadData()
saveDatatoDictionary()
}
}
func saveDatatoDictionary() {
data = NSKeyedArchiver.archivedDataWithRootObject(testArray)
newData = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions(), error: nil) as? NSData
string = NSString(data: newData!, encoding: NSUTF8StringEncoding)
println(string)
}
I would also like to return the JSON string using my savetoDictionart() method.
As it stands you're converting it to data, then attempting to convert the data to to an object as JSON (which fails, it's not JSON) and converting that to a string, basically you have a bunch of meaningless transformations.
As long as the array contains only JSON encodable values (string, number, dictionary, array, nil) you can just use NSJSONSerialization to do it.
Instead just do the array->data->string parts:
Swift 3/4
let array = [ "one", "two" ]
func json(from object:Any) -> String? {
guard let data = try? JSONSerialization.data(withJSONObject: object, options: []) else {
return nil
}
return String(data: data, encoding: String.Encoding.utf8)
}
print("\(json(from:array as Any))")
Original Answer
let array = [ "one", "two" ]
let data = NSJSONSerialization.dataWithJSONObject(array, options: nil, error: nil)
let string = NSString(data: data!, encoding: NSUTF8StringEncoding)
although you should probably not use forced unwrapping, it gives you the right starting point.
Swift 3.0 - 4.0 version
do {
//Convert to Data
let jsonData = try JSONSerialization.data(withJSONObject: dictionaryOrArray, options: JSONSerialization.WritingOptions.prettyPrinted)
//Convert back to string. Usually only do this for debugging
if let JSONString = String(data: jsonData, encoding: String.Encoding.utf8) {
print(JSONString)
}
//In production, you usually want to try and cast as the root data structure. Here we are casting as a dictionary. If the root object is an array cast as [Any].
var json = try JSONSerialization.jsonObject(with: jsonData, options: JSONSerialization.ReadingOptions.mutableContainers) as? [String: Any]
} catch {
print(error.description)
}
The JSONSerialization.WritingOptions.prettyPrinted option gives it to the eventual consumer in an easier to read format if they were to print it out in the debugger.
Reference: Apple Documentation
The JSONSerialization.ReadingOptions.mutableContainers option lets you mutate the returned array's and/or dictionaries.
Reference for all ReadingOptions: Apple Documentation
NOTE: Swift 4 has the ability to encode and decode your objects using a new protocol. Here is Apples Documentation, and a quick tutorial for a starting example.
If you're already using SwiftyJSON:
https://github.com/SwiftyJSON/SwiftyJSON
You can do this:
// this works with dictionaries too
let paramsDictionary = [
"title": "foo",
"description": "bar"
]
let paramsArray = [ "one", "two" ]
let paramsJSON = JSON(paramsArray)
let paramsString = paramsJSON.rawString(encoding: NSUTF8StringEncoding, options: nil)
SWIFT 3 UPDATE
let paramsJSON = JSON(paramsArray)
let paramsString = paramsJSON.rawString(String.Encoding.utf8, options: JSONSerialization.WritingOptions.prettyPrinted)!
JSON strings, which are good for transport, don't come up often because you can JSON encode an HTTP body. But one potential use-case for JSON stringify is Multipart Post, which AlamoFire nows supports.
How to convert array to json String in swift 2.3
var yourString : String = ""
do
{
if let postData : NSData = try NSJSONSerialization.dataWithJSONObject(yourArray, options: NSJSONWritingOptions.PrettyPrinted)
{
yourString = NSString(data: postData, encoding: NSUTF8StringEncoding)! as String
}
}
catch
{
print(error)
}
And now you can use yourSting as JSON string..
Swift 5
This generic extension will convert an array of objects to a JSON string from which it can either be:
saved to the App's Documents Directory (iOS/MacOS)
output directly to a file on the Desktop (MacOS)
.
extension JSONEncoder {
static func encode<T: Encodable>(from data: T) {
do {
let jsonEncoder = JSONEncoder()
jsonEncoder.outputFormatting = .prettyPrinted
let json = try jsonEncoder.encode(data)
let jsonString = String(data: json, encoding: .utf8)
// iOS/Mac: Save to the App's documents directory
saveToDocumentDirectory(jsonString)
// Mac: Output to file on the user's Desktop
saveToDesktop(jsonString)
} catch {
print(error.localizedDescription)
}
}
static private func saveToDocumentDirectory(_ jsonString: String?) {
guard let path = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let fileURL = path.appendingPathComponent("Output.json")
do {
try jsonString?.write(to: fileURL, atomically: true, encoding: .utf8)
} catch {
print(error.localizedDescription)
}
}
static private func saveToDesktop(_ jsonString: String?) {
let homeURL = FileManager.default.homeDirectoryForCurrentUser
let desktopURL = homeURL.appendingPathComponent("Desktop")
let fileURL = desktopURL.appendingPathComponent("Output.json")
do {
try jsonString?.write(to: fileURL, atomically: true, encoding: .utf8)
} catch {
print(error.localizedDescription)
}
}
}
Example:
struct Person: Codable {
var name: String
var pets: [Pet]
}
struct Pet: Codable {
var type: String
}
extension Person {
static func sampleData() -> [Person] {
[
Person(name: "Adam", pets: []),
Person(name: "Jane", pets: [
Pet(type: "Cat")
]),
Person(name: "Robert", pets: [
Pet(type: "Cat"),
Pet(type: "Rabbit")
])
]
}
}
Usage:
JSONEncoder.encode(from: Person.sampleData())
Output:
This will create the following correctly formatted Output.json file:
[
{
"name" : "Adam",
"pets" : [
]
},
{
"name" : "Jane",
"pets" : [
{
"type" : "Cat"
}
]
},
{
"name" : "Robert",
"pets" : [
{
"type" : "Cat"
},
{
"type" : "Rabbit"
}
]
}
]
SWIFT 2.0
var tempJson : NSString = ""
do {
let arrJson = try NSJSONSerialization.dataWithJSONObject(arrInvitationList, options: NSJSONWritingOptions.PrettyPrinted)
let string = NSString(data: arrJson, encoding: NSUTF8StringEncoding)
tempJson = string! as NSString
}catch let error as NSError{
print(error.description)
}
NOTE:- use tempJson variable when you want to use.
extension Array where Element: Encodable {
func asArrayDictionary() throws -> [[String: Any]] {
var data: [[String: Any]] = []
for element in self {
data.append(try element.asDictionary())
}
return data
}
}
extension Encodable {
func asDictionary() throws -> [String: Any] {
let data = try JSONEncoder().encode(self)
guard let dictionary = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any] else {
throw NSError()
}
return dictionary
}
}
If you're using Codable protocols in your models these extensions might be helpful for getting dictionary representation (Swift 4)
Hint: To convert an NSArray containing JSON compatible objects to an NSData object containing a JSON document, use the appropriate method of NSJSONSerialization. JSONObjectWithData is not it.
Hint 2: You rarely want that data as a string; only for debugging purposes.
For Swift 4.2, that code still works fine
var mnemonic: [String] = ["abandon", "amount", "liar", "buyer"]
var myJsonString = ""
do {
let data = try JSONSerialization.data(withJSONObject:mnemonic, options: .prettyPrinted)
myJsonString = NSString(data: data, encoding: String.Encoding.utf8.rawValue) as! String
} catch {
print(error.localizedDescription)
}
return myJsonString
Swift 5
Make sure your object confirm Codable.
Swift's default variable types like Int, String, Double and ..., all are Codable that means we can convert theme to Data and vice versa.
For example, let's convert array of Int to String Base64
let array = [1, 2, 3]
let data = try? JSONEncoder().encode(array)
nsManagedObject.array = data?.base64EncodedString()
Make sure your NSManaged variable type is String in core data schema editor and custom class if your using custom class for core data objects.
let's convert back base64 string to array:
var getArray: [Int] {
guard let array = array else { return [] }
guard let data = Data(base64Encoded: array) else { return [] }
guard let val = try? JSONDecoder().decode([Int].self, from: data) else { return [] }
return val
}
Do not convert your own object to Base64 and store as String in CoreData and vice versa because we have something that named Relation in CoreData (databases).
For Swift 3.0 you have to use this:
var postString = ""
do {
let data = try JSONSerialization.data(withJSONObject: self.arrayNParcel, options: .prettyPrinted)
let string1:String = NSString(data: data, encoding: String.Encoding.utf8.rawValue) as! String
postString = "arrayData=\(string1)&user_id=\(userId)&markupSrcReport=\(markup)"
} catch {
print(error.localizedDescription)
}
request.httpBody = postString.data(using: .utf8)
100% working TESTED
You can try this.
func convertToJSONString(value: AnyObject) -> String? {
if JSONSerialization.isValidJSONObject(value) {
do{
let data = try JSONSerialization.data(withJSONObject: value, options: [])
if let string = NSString(data: data, encoding: String.Encoding.utf8.rawValue) {
return string as String
}
}catch{
}
}
return nil
}

Resources