In my tests I'm used to write Strings in arrays in different lines like
let jsonString = ["{"
,"\"url\": \"http://localhost:8090/rest/api/3\","
, "\"id\": \"3\","
, "\"description\": \"A test that needs to be done.\","
, "\"name\": \"Test\","
, "\"subtest\": false,"
, "\"avatar\": 1"
,"}"].reduce("", combine: +)
That works fine, still my array get 145 lines for a large test json string.
With 145 lines (or maybe less, didn't tried it line by line) the build task hangs while "Compiling Swift source files".
First, that is a bit crazy. 30 lines are ok, 145 not? What?
Second, what is a better solution to write a String in multiple lines in Swift? - I don't want to load a json and parse it from an external file.
This is probably because of type inference.
Try giving an explicit [String] type to an array variable to help the compiler figure it out, then apply reduce to the variable.
let arrayOfStrings: [String] = ["{"
,"\"url\": \"http://localhost:8090/rest/api/3\","
, "\"id\": \"3\","
, "\"description\": \"A test that needs to be done.\","
, "\"name\": \"Test\","
, "\"subtest\": false,"
, "\"avatar\": 1"
,"}"] // etc
let jsonString = arrayOfStrings.reduce("", combine: +)
Anyway, to create JSON you should make a dictionary then serialize it with NSJSONSerialization, this is less error prone:
Swift 2
do {
// Create a dictionary.
let dict = ["url": "http://localhost:8090/rest/api/3", "id": "3"] // etc
// Encode it to JSON data.
let jsonData = try NSJSONSerialization.dataWithJSONObject(dict, options: [])
// Get the object back.
if let jsonObject = try NSJSONSerialization.JSONObjectWithData(jsonData, options: []) as? [String:String] {
print(jsonObject)
}
// Get it as a String.
if let jsonString = String(data: jsonData, encoding: NSUTF8StringEncoding) {
print(jsonString)
}
} catch let error as NSError {
print(error)
}
Swift 3
do {
// Create a dictionary.
let dict = ["url": "http://localhost:8090/rest/api/3", "id": "3"] // etc
// Encode it to JSON data.
let jsonData = try JSONSerialization.data(withJSONObject: dict, options: [])
// Get the object back.
if let jsonObject = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [String:String] {
print(jsonObject)
}
// Get it as a String.
if let jsonString = String(data: jsonData, encoding: String.Encoding.utf8) {
print(jsonString)
}
} catch let error as NSError {
print(error)
}
Related
This question already has an answer here:
JSON with "(" ")" brackets istead of "[" "]" [closed]
(1 answer)
Closed 5 years ago.
When I add an array in my JSON object and convert it to JSON, after printing there is parenthesis instead of curly brace.
var jsonObject : [String: Any] = [:]
var x = [1,2,3,4]
jsonObject["arr"] = x
do{
let jsonData = try JSONSerialization.data(withJSONObject: jsonObject, options: [])
let decoded = try JSONSerialization.jsonObject(with: jsonData, options: [])
print(decoded)
} ...
And the result of printing:
{
arr = (
1,
2,
3,
4
);
}
But in JSON, to show an array it uses [ instead of (.
What's wrong?
You are converting the dictionary(!) to JSON and then you convert it back and print it.
To print the JSON string you have to write
let jsonData = try JSONSerialization.data(withJSONObject: jsonObject)
let jsonString = String(data: jsonData, encoding: .utf8)
print(jsonString ?? "Wrong data")
When you print a Swift object, Swift debugger is free to print it however it wants. It's just for convenience, there are no guarantees about the format. So, it prints arrays in parenthesis.
When you convert it to JSON, on the other hand, it creates a JSON string, and JSON has a specified format for arrays, which includes square brackets.
UPDATE: after reading the question again, it seems to me you're under the impression that you're printing a JSON representation of your object. You're not. What you do currently is you convert your Swift object to JSON, and then convert it back to a Swift object. And then you print the resulting Swift object (see paragraph 1 of my answer).
So, if your goal is to print the JSON representation, you need to convert jsonData to String (see other answers), and print that string. You shouldn't use NSJSONSerialization again for backward conversion.
You should use this to print your JSON.
public extension Collection {
/// Convert self to JSON String.
/// - Returns: Returns the JSON as String or empty string if error while parsing.
func json() -> String {
do {
let jsonData = try JSONSerialization.data(withJSONObject: self, options: [.prettyPrinted])
guard let jsonString = String(data: jsonData, encoding: String.Encoding.utf8) else {
print("Error creating string with data.")
return "{}"
}
return jsonString
} catch let parseError {
print("json serialization error: \(parseError)")
return "{}"
}
}
}
The conversion is correct, It's just that you are printing decoded version, which is swift dictionary, not JSON.
var jsonObject : [String: Any] = [:]
var x = [1,2,3,4]
jsonObject["arr"] = x
print(jsonObject.json())
do{
let jsonData = try JSONSerialization.data(withJSONObject: jsonObject, options: [])
let decoded = try JSONSerialization.jsonObject(with: jsonData, options: [])
print(decoded)
}
Output:-
{
"arr" : [
1,
2,
3,
4
]
}
I added some print clauses to you code, and pasted the console log as comments.
var jsonObject : [String: Any] = [:]
var x = [1,2,3,4]
jsonObject["arr"] = x
print(type(of: jsonObject)) // Dictionary<String, Any>
do{
let jsonData = try JSONSerialization.data(withJSONObject: jsonObject, options: .prettyPrinted)
let decoded = try JSONSerialization.jsonObject(with: jsonData, options: .allowFragments)
print(type(of: decoded)) // __NSSingleEntryDictionaryI
let str = String(data: jsonData, encoding: .utf8)
print(str!) /* {
"arr" : [
1,
2,
3,
4
]
}*/
}
As you can see, the type of decoded is __NSSingleEntryDictionaryI, so its result of printing is not as what you expected, with "(" instead of "[". If you want the right representation in JSON, you should convert the jsonData to String.
Swift n00b here.
I'm getting something like this as a response from a server:
[
{
"foo": [],
"bar":"asdf",
...
}
]
Now I understand how to parse regular JSON, but not when it has an array as the base element.
Here is code I used so far, which would work for regular JSON:
let task = session.dataTask(with: urlRequest) {
(data, response, error) in
// check for any errors
guard error == nil else {
print("error calling POST on \(String(describing: urlRequest.url?.absoluteURL))")
print(error!)
return
}
// make sure we got the data
guard let responseData = data else {
print("Error: did not receive data")
return
}
let responseString = String(data: responseData, encoding: String.Encoding.utf8) as String!
// parse the result as JSON, since that's what the API provides
do {
guard let todo = try JSONSerialization.jsonObject(with: responseData, options: [])
as? [String: Any] else {
print("error trying to convert data to JSON")
return
}
... // do whatever with the response
} catch {
print("an error occurred")
return
}
}
task.resume()
The error I'm getting from that is "error trying to convert data to JSON.
The easiest way to parse that response I can think of is making a substring from 1 to length - 1 and then parsing it, but that doesn't seem particularly safe.
Is there any way I can parse that response into a [Dictionary]?
Your JSON is regular JSON. An array at the top level is just as valid (and regular) as a dictionary.
Simply update your cast accordingly:
guard let todo = try JSONSerialization.jsonObject(with: responseData, options: [])
as? [[String: Any]] else {
That indicates that you have an array of dictionary. Now you can iterate the array and get each dictionary as needed.
I have array of dictionary [[String:Any]] i want to convert it to JSON string. but i don't know how to start with it. I tried JSONSerialization.data(withJSONObject: array, options: .prettyPrinted) and i pass array to method but it show error. Any solution please comment below. Thank.
Try as following code...
do {
//Convert to Data
let jsonData = try JSONSerialization.data(withJSONObject: dictionaryArray, options: JSONSerialization.WritingOptions.prettyPrinted)
//Do this for print data only otherwise skip
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 [AnyObject].
var json = try JSONSerialization.jsonObject(with: jsonData, options: JSONSerialization.ReadingOptions.mutableContainers) as? [String: AnyObject]
} catch {
print(error.description)
}
I am using Swift and Xcode, I have built model object with the following variables:
var itemImageNames: [String]?
var itemTitle: String?
var itemDescription: String?
var itemURL: String?
In the mainviewcontroller, I created an variable of model type. I am initiating a NSURLSession...dataTaskWithURL... and adding itemImageNames that I receive back from the server by using append. The data comes back as valid, I've parsed it and it is indeed coming back as Strings. I've tried two solutions,
create a string array out of the images and set that array to self.item.itemImageNames?
do {
if let json = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? [[String: AnyObject]] {
var imageURLs: [String] = [""]
for dictionary in json {
if let imageURL = dictionary["url"] as? String {
imageURLs.append(imageURL)
print(imageURL)
}
}
self.featuredItem.itemImageNames? = imageURLs
append each of the strings as I get them using self.item.itemImageNames?.append(image)
do {
if let json = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? [[String: AnyObject]] {
for dictionary in json {
if let imageURL = dictionary["url"] as? String {
self.featuredItem.itemImageNames?.append(imageURL)
print(imageURL)
}
}
For some reason, the itemImageNames remains nil, using both approaches. I am sure it will work if I just use one image (e.g. the 1st image), and change itemImageNames to a "String?".
In addition, I can update the itemTitle, itemDescription, and itemURL easily by just setting them to self.item.itemTitle, self.item.itemDescription, self.item.itemURL, respectively. Is there something I'm missing on how to enter information into an array?
In approach #2 initialize the itemImageNames array before trying to append to it. If you try to append to an array that is nil then nothing will happen.
itemImageNames = []
for dictionary in json {
if let imageURL = dictionary["url"] as? String {
self.featuredItem.itemImageNames?.append(imageURL)
print(imageURL)
}
}
I have [AnyObject] array
var updatedPos = [AnyObject]()
I am setting data in that according to my requirement like!
let para:NSMutableDictionary = NSMutableDictionary()
para.setValue(posId, forKey: "id")
para.setValue(posName, forKey: "job")
let jsonData = try! NSJSONSerialization.dataWithJSONObject(para, options: NSJSONWritingOptions())
let jsonString = NSString(data: jsonData, encoding: NSUTF8StringEncoding) as! String
self.updatedPos.append(jsonString)
Now in my code i have some requirement to remove the object from this array where id getting matched according to requirement Here is the code which i am trying to implement
for var i = 0; i < updatedPos.count; i++
{
let posItem = updatedPos[i]
print("Id=\(posItem)")
let pId = posItem["id"] as? String
print("secRId=\(pId)")
if removeId! == pId!
{
updatedPos.removeAtIndex(i)
}
}
Here print("Id=\(posItem)") give me output asId={"id":"51","job":"Programmer"} but here i am not able to access id from this object. here print("secRId=\(pId)") give me nil
First of all use native Swift collection types.
Second of all use types as specific as possible.
For example your [AnyObject] array can be also declared as an array of dictionaries [[String:AnyObject]]
var updatedPos = [[String:AnyObject]]()
Now create the dictionaries and add them to the array (in your example the dictionary is actually [String:String] but I keep the AnyObject values).
let para1 : [String:AnyObject] = ["id" : "51", "job" : "Programmer"]
let para2 : [String:AnyObject] = ["id" : "12", "job" : "Designer"]
updatedPos.append(para1)
updatedPos.append(para2)
If you want to remove an item by id use the filter function
let removeId = "12"
updatedPos = updatedPos.filter { $0["id"] as? String != removeId }
or alternatively
if let indexToDelete = updatedPos.indexOf{ $0["id"] as? String == removeId} {
updatedPos.removeAtIndex(indexToDelete)
}
The JSON serialization is not needed for the code you provided.
PS: Never write valueForKey: and setValue:forKey: unless you know exactly what it's doing.
After some little bit stuffs I have found the very easy and best solution for my question. And I want to do special thanks to #vadian. Because he teach me new thing here. Hey Thank you very much #vadian
Finally the answer is I had covert posItem in json Format for finding the id from Id={"id":"51","job":"Programmer"} this string
And the way is
let data = posItem.dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: false)
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)
if let dict = json as? [String: AnyObject] {
let id = dict["id"]
if removeId! == id! as! String
{
updatedLoc.removeAtIndex(i)
}
}
}
catch {
print(error)
}