I am trying to send a request to an API that requires this format:
{
"inputs": [
{
"data": {
"image": {
"base64": "iVBORw0KGgoAAAIVnFT/DPAAAAAElFTkSuQmCC..."
}
}
}
]
}
This is what I tried in Swift 3:
let base64 = ["base64": "1010101..."]
let image: [String: Any] = ["image": base64]
let data: [String: Any] = ["data": image]
var input = [Dictionary<String, Any>]()
input = [data]
let jsonData = try! JSONSerialization.data(withJSONObject: input, options: .prettyPrinted)
I got an invalid request error when trying to hit the endpoint. Not surprising - when I printed it:
let jsonString = try! JSONSerialization.jsonObject(with: jsonData, options: [])
print(jsonString)
It produced a string like this:
(
{
data = {
image = {
base64 = 1010101;
};
};
}
)
For now, I am rolling my own string. It works...but it's ugly as hell and just really not tenable.
let imageAsString = "{\"inputs\": [{\"data\": {\"image\": {\"base64\": \"\(strBase64)\"}}}]}"
My other thought was that I could create a series of embedded objects: a Data object that holds a dictionary; an Image object that holds a Data object; and an Inputs object that holds an array of Data objects.
class Image {
var base64 = Dictionary<String, String>()
}
class Data {
var image = Image()
}
class Inputs {
var inputs = [Data]()
}
But initializing them all and calling JSONSerialization.data doesn't work as this takes a dictionary object.
I'm not really interested in pulling in 3rd party plugins for now. Any ideas on how to convert these nested objects into json using the native iOS libraries?
Absolutely! Using the Codable protocol and JSONEncoder in Swift 4 will do the trick:
struct ImageObj: Codable {
let base64: String
}
struct DataObj: Codable {
let image: ImageObj
}
struct InputObj: Codable {
let data: DataObj
}
struct InputsContainerObj: Codable {
let inputs: [InputObj]
}
let imageObj = ImageObj(base64: "abc123")
let dataObj = DataObj(image: imageObj)
let inputObj = InputObj(data: dataObj)
let inputsContainerObj = InputsContainerObj(inputs: [inputObj])
let encoder = JSONEncoder()
do {
let jsonData = try encoder.encode(inputsContainerObj)
let jsonString = String(data: jsonData, encoding: .utf8)!
print(jsonString) //{"inputs":[{"data":{"image":{"base64":"abc123"}}}]}
} catch _ as NSError {
}
If Swift 3 is your only option, then you will have to make JSONSerialization.data work:
let dict = ["inputs": [["data": ["image": ["base64": "abc123"]]]]]
do {
let jsonData = try JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted)
let jsonString = String(data: jsonData, encoding: .utf8)!
print(jsonString)
/*
{
"inputs" : [
{
"data" : {
"image" : {
"base64" : "abc123"
}
}
}
]
}
*/
} catch _ as NSError {
}
I'm having issues trying to access a string inside of a JSON dictionary, Here is the JSON data, the string I'm trying to access is the "link" value :
channels = (
{
social = {
facebook = {
"facebook_id" = 47360808996;
link = "https://www.facebook.com/CBS";
};
twitter = {
link = "https://twitter.com/CBS";
"twitter_id" = 97739866;
};
};
}
);
Here is the custom objects I created which keeps giving me an error
" Type '(key: String, value: AnyObject)' has no subscript members"
class SocialInfo: NSObject, Mappable {
var facebook: String?
var facebookDict: String?
required init?(map: Map) {
}
func mapping(map: Map) {
///transforms string to dictionary [string: anyobject] - only used for specific JSON data name: (facebook) - (link)
let facebookTransform = TransformOf<String, [String: AnyObject]> (fromJSON: { (value: [String: AnyObject]?) -> String? in
if let value = value {
for dict in value {
if let social = dict["social"] as? NSDictionary {
if let fb = social["facebook"] as? [String: AnyObject]{
if let fbLink = fb["link"] as? String{
return fbLink
}
}
}
}
}
return nil
}, toJSON: { (value: String?) -> [String: AnyObject]? in
return nil
})
facebookDict <- (map["channels"], facebookTransform)
facebook <- map["link"]
}
}
Here is my JSON call function to get info:
func getSocialInfo (_ completion: (([SocialInfo]) -> Void)?) {
Alamofire.request("\(baseURL)/\(apiKey)/show/950", method: .get, encoding: JSONEncoding.default).responseArray { (response: DataResponse<[SocialInfo]>) in
if let channelsResponse = response.result.value {
completion?(channelsResponse)
}
}
}
For reference I recreated this from a working project using NSURLSession the below code works:
if let channels = jsonResult["channels"] as? [[String:AnyObject]], !channels.isEmpty {
let channel = channels[0] // now the compiler knows it's [String:AnyObject]
let social = channel["social"] as? NSDictionary
let facebookDict = social!["facebook"] as? [String:AnyObject]
let facebook = nullToNil(facebookDict!["link"]) as? String ?? "N/A"
let twitterDict = social!["twitter"] as? [String:AnyObject]
let twitter = nullToNil(twitterDict!["link"]) as? String ?? "N/A"
I will assume you use Swift 3, as Swift 2 is deprecated.
You should avoid NSDictionary and AnyObject when parsing JSON in Swift 3. Simply use [String: Any] everywhere instead.
After previous issues with this particular app code as 'solved' with help, a new issue has arisen with this code:
var properties: [String] = []
var unsortedDetails: [String] = []
var contacts: [[String]] = []
var propertySorting: [(key: String, ind: String, link: Bool)] = []
var autolinks: [Bool] = []
and, in the viewDidLoad function:
let contactsRequest = URLRequest(url: URL(string: "https://script.google.com/macros/s/AKfycbxOLElujQcy1-ZUer1KgEvK16gkTLUqYftApjNCM_IRTL3HSuDk/exec?id=" + ID + "&sheet=" + Industry.replacingOccurrences(of: " ", with: "+", options: NSString.CompareOptions.literal, range: nil))!)
let contactsTask = URLSession.shared.dataTask(with: contactsRequest as URLRequest)
{(data, response, error) in
do {
let jsonResult: Dictionary = try JSONSerialization.jsonObject(with: data!, options: []) as! [String : Any]
let results = jsonResult[self.Industry] as! [[String : Any]]
for key in results[0].keys {
self.propertySorting.append(key: (key as? String)!, ind: results[0].value(forKey: key as! String) as! String, link: results[1].value(forKey: key as! String) as! String != "X")
}
self.propertySorting.sort(by: {Int($0.ind) < Int($1.ind)})
for property in self.propertySorting {
self.properties.append(property.key)
self.autolinks.append(property.link)
}
for contact in results {
self.contacts.append([])
for key in self.properties {
self.contacts[self.contacts.count-1].append(contact.value(forKey: key) as! String)
}
}
DispatchQueue.main.async(execute: {
let listController = self.viewControllers[0] as! ListController
listController.properties = self.properties
listController.contacts = self.contacts
listController.autolinks = self.autolinks
listController.tableView.reloadData()
})
} catch {
print("FATAL ERROR")
}
}
contactsTask.resume()
The issues now encountered are:
Extra argument 'ind' in call when propertySorting.append(...) is called, and if this (previously working) ind section is deleted, the same error is returned for link
Instead of NSArray and NSDictionary used Swift native Array and Dictionary and use keys with Dictionary to get array of all keys.
let jsonResult: Dictionary = try JSONSerialization.jsonObject(with: data!, options: []) as! [String : Any]
let results = jsonResult[self.Industry] as! [[String : Any]]
for key in results[0].keys {
}
Note: There is no need to use .mutableContainers option with Swift native type object.
I am attempting to convert a NSURLRequest's HTTPBody from NSData to a Dictionary, so that access it's parameters and run a check.
var parameters: [NSObject: AnyObject?] = [
"method": "foo",
]
var urlRequestString = "www.foobar.com/api/aMethod"
var urlRequest = AFOAuth2Manager().requestSerializer.requestWithMethod("POST",
URLString:urlRequestString ,
parameters: postParameters,
error: nil)
in another method, I am attempting to run a check on the urlRequest being constructed above to ensure that the "method" in the NSURLRequest contain's the right value "foo".
What i need is to convert the HTTPBody to a Dictionary ( [NSObject: AnyObject])
so that i can check to see if the "method" parameter contains the value I need.
How can i achieve this?
Try this:
func queryDictionaryForQueryString(query: String) -> Dictionary<String, String> {
var dictionary = [String: String]()
for keyValueString in query.componentsSeparatedByString("&") {
var parts = keyValueString.componentsSeparatedByString("=")
if parts.count < 2 { continue; }
var key = parts[0].stringByRemovingPercentEncoding!
var value = parts[1].stringByRemovingPercentEncoding!
dictionary[key] = value
}
return dictionary
}
then you can
let httpBody = urlRequest.HTTPBody
let httpBodyString = NSString(data: httpBody!, encoding: NSUTF8StringEncoding) as! String
let queryDictionary = queryDictionaryForQueryString(httpBodyString)
println(queryDictionary["method"])
Here's my more Swifty 3.0 NSURL extension to accomplish this with nil safety:
extension NSURL {
var paramsDictionary: [String : String]? {
guard let parameterString = parameterString else {
return nil
}
var dictionary: [String : String] = [:]
parameterString.components(separatedBy: "&").forEach {
let componants = $0.components(separatedBy: "=")
guard let name = componants[0].removingPercentEncoding, let value = componants[1].removingPercentEncoding else {
return
}
dictionary[name] = value
}
return dictionary.isEmpty ? nil : dictionary
}
}
var url = NSURL(string: "https://www.testWebSite.com/index.php;param=value&testParam=test&thingParam=thing")
let params = url?.paramsDictionary //results in "Optional(["thingParam": "thing", "testParam": "test", "param": "value"])"
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
}