SwiftyJSON keeps returning empty objects - ios

I am new to SwiftyJSON, and I'm having some trouble with it. I can get it to return the entire JSON file as a string, but the moment I try to parse it, I keep getting empty variables back, and I'm not sure what I'm doing wrong.
This is the formatting of my JSON file:
[
{
"entryID": 1,
"from": "String",
"to": "String",
"value": "String"
},
{
...
},
...
]
And this is roughly what I want to do with it (in quite inelegant code, I do apologise, I'm new to Swift):
for entry: JSON in indexJSON.arrayValue {
var vEntryID: Int
var vFrom: String
var vTo: String
var vValue: String
for (dictKey: String, dictVal: JSON) in entry.dictionaryValue {
if(dictKey=="entryID") {vEntryID = dictVal.intValue}
if(dictKey=="from") {vFrom = dictVal.stringValue}
if(dictKey=="to") {vTo = dictVal.stringValue}
if(dictKey=="value") {vValue = dictVal.stringValue}
}
someSwiftObject[vEntryID]["from"] = vFrom
someSwiftObject[vEntryID]["to"] = vTo
someSwiftObject[vEntryID]["value"] = vValue
}
However, this block never executes at all, because indexJSON.arrayValue is always empty.
When I try to run the following, it correctly prints the complete file contents to the console:
let indexJSON = JSON(content!)
println(indexJSON.stringValue)
But when I try to go deeper, to fetch any element, it returns nothing:
if(indexJSON.arrayValue.isEmpty==true) {println("indexJSON.arrayValue is Empty")}
if(indexJSON[0].arrayValue.isEmpty==true) {println("indexJSON[0].arrayValue is Empty")}
if(indexJSON[0].dictionaryValue.isEmpty==true) {println("indexJSON[0].dictionaryValue is Empty")}
if(indexJSON[0]["entryID"]==nil) {println("indexJSON[0][\"entryID\"].stringValue is Empty")}
Output:
indexJSON.arrayValue is Empty
indexJSON[0].arrayValue is Empty
indexJSON[0].dictionaryValue is Empty
indexJSON[0]["entryID"].stringValue is Empty
I'd be grateful for any help! What am I doing wrong?

I checked SwiftyJSON source code and I think I know where the problem is.
I suppose that you are using String to initialize the JSON object like this
let s = "{\"entryID\": 1,\"from\": \"String\",\"to\": \"String\",\"value\": \"String\"}"
let j = JSON(s)
In this case the JSON object is actuall given a type "String", not Array. That's why it's not iterable and its arrayValue is empty.
To do what you want to do, you need to initialize it with an Array object:
let arr = [
[
"entryID":1,
"from":"String",
"to":"String",
"value":"String",
]
]
let j2 = JSON(arr)
Now j2 is an array JSON object and iterable.
SwiftyJSON can only be initialized with NSData and object. So if you want to initialize it with a String you need to do this:
if let data = s.dataUsingEncoding(NSUTF8StringEncoding) {
let j = JSON(data:data)
println(j)
}

first of all, make sure the format of your json string is correct. in your question, your json string is a array, just format the string like this(the content is from my code):
let jsonStr = "[{\"name\": \"hangge\", \"age\": 100, \"phones\": [{\"name\": \"公司\",\"number\": \"123456\"}, {\"name\": \"家庭\",\"number\": \"001\"}]}, {\"name\": \"big boss\",\"age\": 1,\"phones\": [{ \"name\": \"公司\",\"number\": \"111111\"}]}]"
then you can use SwityJson to get the array object, like this:
let strData = jsonStr.data(using: String.Encoding.utf8, allowLossyConversion: false)
let json = JSON(data: strData!)
for object in json.arrayValue {
let name = object["name"].string
}

Take a look at the documentation here: https://github.com/lingoer/SwiftyJSON#loop
You are iterating it incorrectly. You should be iterating over the array with a for loop like this:
for (index: String, subJson: JSON) in json {
//Do something you want
}

Related

Swifty Json parsing

I am using SwiftyJson library for parsing my following json
{
"data": {
"id": "12345",
"messages": {
"message": "{\"data\":{\"msg\":\"HelloMsg\"}}"
}
}
}
I tried to use following code to get msg parameter
let json = JSON(data)
let msg = JSON(json["data"]["messages"]["message"])
msg["data"]["msg"].stringValue
However, I could not get the value of msg parameter. What shall I do to get HelloMsg?
The content of the "message" field is not parsed JSON, it's a JSON string.
Use SwiftyJSON's JSON(parseJSON:) initializer to accept a string as input and parse it as JSON:
let messages = json["data"]["messages"]["message"].stringValue
let innerJSON = JSON(parseJSON: messages)
let msg = innerJSON["data"]["msg"].stringValue // "HelloMsg"
The error occurs because JSON(...) is the wrong API to initialize and parse a SwiftyJSON object from a string.
You have to use this syntax:
let json = JSON(data)
let msg = JSON(parseJSON: json["data"]["messages"]["message"].stringValue)
msg["data"]["msg"].stringValue
From the documentation of init(_ object: Any):
note: this does not parse a String into JSON, instead use init(parseJSON: String)
Edit:
To test the code in a Playground
let str = """
{"data": {"id": "12345",
"messages": {
"message": "{\\"data\\":{\\"msg\\":\\"HelloMsg\\"}}"
}
}
}
"""
let data = Data(str.utf8)
let json = JSON(data)
let msg = JSON(parseJSON: json["data"]["messages"]["message"].stringValue)
msg["data"]["msg"].stringValue
The JSON as traditional literal string is
let str = "{\"data\": {\"id\": \"12345\",\"messages\": {\"message\": \"{\\\"data\\\":{\\\"msg\\\":\\\"HelloMsg\\\"}}\"}}}"
The messaage is a string. not a JSON. so SwiftyJson could not parse it. You will have to first parse that string and than get the message from that using JSONSerialization.jsonObject(with: Data, options: JSONSerialization.ReadingOptions).
You can refer to this answer to get the dictionary from that string: https://stackoverflow.com/a/30480777/7820107
Your second "message" key value is a String with a dictionary in JSON format, so you need to convert that string to JSON and access to ["data"]["msg"] then
Code
let json = JSON(data)
let msg = json["data"]["messages"]["message"]
let jsonFromString = JSON(data: msg.data(using: .utf8)!, options: JSONSerialization.ReadingOptions.allowFragments, error: nil)
debugPrint(jsonFromString["data"]["msg"])
Output
HelloMsg

swift parse json as per maintaining order

Suppose i have json string in which there is a json array called data.
The array holds json object of user profile data for example name,age,gender etc.
Now want to parse that json object as per order, for example if the object is
{
"name": "sample name",
"age": "30",
"gender": "male"
}
i want to parse the list as ordered like name,age,gender but with ios,when i convert the json object as dictionary , the order is changed,i know dictionary is not ordered so what is the the alternative to achieve this?
its a third party api so i dont have any hand on it,we have done it in android with linked hash map,but really stuck in swift , the last thing i would want to do is parse with regular expression.
im parsing the json in following way :
var rootData = try JSONSerialization.jsonObject(with: data!) as! [String:Any]
if let val = fromList["data"] {
let dataNode = val as! [[String:Any]]
for row in dataNode {
for (key,keyVal) in row {
//here the key is not in order.because when we cast it as dictionary the order gets changed.
}
}
For android we have achieved to do this with following function :
public ArrayList<LinkedHashMap<String, Object>> parseJsonArrayList(String odata, String arrayName) {
ArrayList<LinkedHashMap<String, Object>> mylist = new ArrayList<>();
try {
JSONObject e = new JSONObject(odata);
JSONArray data = e.getJSONArray(arrayName);
for(int i = 0; i < data.length(); ++i) {
JSONObject v = data.getJSONObject(i);
LinkedHashMap<String, Object> map = new LinkedHashMap<>(100, 0.75f, false);
Iterator keys = v.keys();
while(keys.hasNext()) {
String key = String.valueOf(keys.next());
//gph.log("debug4", key);
map.put(key, v.getString(key));
//gph.log("debug4", v.getString(key));
}
mylist.add(map);
}
} catch (JSONException var10) {
var10.printStackTrace();
}
return mylist;
}
Don’t try to order the dictionary. Instead create an array of the keys in the order you desire:
let keys = [“name”, “age”, “gender”]
Then access the dictionary with them:
for key in keys {
let value = dict[key]
// Present the value.
}
That will ensure the order you expect.
As you mentioned you cannot get the ordered data in Dictionary. If possible you can add the "order" key in your JSON like
[
{
"name": "sample name",
"order": 1
},
{
"age": "30",
"order": 1
},
{
"gender": "",
"male": "",
"order": 1
}
]
so that based on the order key you can do the sorting.

Taking out array from dictionary in swift 3

Hi I am trying to populate a view using the response obtained from service but not able to fetch the exact value out of the whole response ,
[
["product_id": PRO161519,
"name": clothes,
"brand_name": Levis,
"discountprice": 0,
"images": <__NSArrayM 0x6000002541c0>(
{
image = "HTTP://i.vinove.com/dnn/backend/uploads/954tshirt_PNG5434.png";
}
)
"category": Accessories,
"price": 23.00
]
]
ProductList-Model
import UIKit
import SpeedLog
let KImages = "images"
let KListImage = "image"
struct ProductList{
var images = ""
var itemArray = [String]()
func bindProductListDataToPopulateView(_ response:[[String:Any]])->[ProductList]{
SpeedLog.print("response value as result",response)
for items in response{
print("items values",items)
}
print("item array",itemArray)
return []
}
}
response value as result
[["image":
item Values
["image":
Kindly help me to get the values images here.
You have to use like this :
for product in products {
if let productImages = product["images"], let images = productImages as? NSArray {
for image in images {
if let image = image as? [String: String] {
print(image["image"])
}
}
}
}
More than likely that JSON response you posted will eventually find its way to you in the form of a key-value Dictionary. You then use a "key" from the JSON you posted to extract the key's corresponding "value". In the snippet you posted, the keys would be the values on the left of the colon (e.g. "product_id", "name", etc).
Now, lets say your dictionary of key-values was called "jsonDictionary". You then would extract the values like so:
let productId = jsonDictionary["product_id"]
let name = jsonDictionary["name"]
If, however, you don't have logic to deserialize that raw JSON data (that you posted in your question) into a Dictionary, then you'll have to start there instead.

JSQMessageData append Json data

I am trying to add json values in JSQMessageData to show the message on JSQMessagesViewController. The view is set up and this the lite chat(can chat only once).We use an api to send and receive messages. The problem is when I fetched data from api as json it returns the value. I want to append that json data to the rest of my JSQMessages objects, I tried the last few days and have failed to accomplish this. Here is the full code and json response.
APIHandler.requestGETURL(urlString, success: { (JSON) in
print(JSON)
// var messageDictionary : [JSQMessageData] = []
// this is the message object
// i want to add the json data to my messageDictionary
// reload collection view
/*
{
"message_time" : "27-05-2017",
"user_id" : 1924,
"user_name" : "Tester name",
"message" : "hi",
"user_thumb" : "<image_path>"
},
{
"message_time" : "27-05-2017",
"user_id" : 1924,
"user_name" : "Tester name",
"message" : "how are you?",
"user_thumb" : "<image_path>"
}
*/
// i want to
let arrayNames = JSON["data"]
self.messageDictionary.append(JSQMessageData())
// I am stuck here
}) { (Error) in
print(Error.localizedDescription)
}
If I understand you correctly you're trying to parse json into a JSQMessage object. Your message data is not overly complex, it contains all the things a standard JSQMessage needs. So there is not any reason to create your own JSQMessageData object. You can just use one of the JSQMessage initializers. Since you are only using "Text" messages and not any other "Media" the
JSQMessage(senderId: <String!>, senderDisplayName: <String!>, date: <#Date>, text: <String>)
should be all you need. So all you need to do is get the values out of your json response. There are many ways to do this.
I am going to assume that the json you provided is also wrapped in a list like this
[
{ "message_time" : "27-05-2017",
"user_id" : 1924,
"user_name" : "Tester name",
"message" : "hi",
"user_thumb" : "<image_path>"
},
{ "message_time" : "27-05-2017",
"user_id" : 1924,
"user_name" : "Tester name",
"message" : "how are you?",
"user_thumb" : "<image_path>"
}
]
We can utilise the flatmap function to get our "Messages" out of the json data. You also do not need a dictionary becasue there is not key for each message so just use a list that contains JSQMessageObjects
var messages:[JSQMessages] = []
var imageDictionary: [userID: String: imagePath: String] = [:]
APIHandler.requestGETURL(urlString, success: { (JSON) in
print(JSON)
let messagesJSON = response.result.value as? [[String: Any]] ?? [[:]]
guard let listOfMessages = JSON as? [[String: AnyObject]]
messages: [JSQMessage] = listOfMessages.flatmap { messageData in
guard let dateCreated = messageData["message_time"] as? Date,
let userID = messageData["user_id"] as? String,
let userName = messageData["user_name"] as? String,
let text = messageData["message"] as? String else {
//If any of these things are missing we do not want to save the entry
return nil
}
let imagePath = messageData["user_thumb"] as? String
imageDictionary[userID] = imagePath
return JSQMessage(senderId: userID, senderDisplayName: userName, date: dateCreated, text: text)
}) { (let error: Error) in
if error != nil {
print(Error.localizedDescription)
}
}
I would save your image paths into a dictionary and fetch them on a background thread that way users can view the messages while the images populate as they arrive. Then once they have loaded apply them to your messages. or you can add it to your own custom message object that conformes to the JSQMessageDataSource protocol. for more on how to accomplish that check out this post
#Daniel is saying right, your json is enough simple that you don't need to add any JSQMessageData and maybe you are actually doing some extra effort, i have faced similar kind of problem when i need to pass a NSDictionary object with JSQMessage Objects so i used a tricky way for doing that ( and it works perfectly fine :) )
not sure about your case but this helps me a lot in many situations so follow these steps :
convert your json data into string
now save this json string into the accessebilityHint property of JSQmessage object. like -
(jsqmessageObj).accessibilityHint = jsonString
as according to your need as you want to use this json just extract the JSQmessage Object (like in cellForRowAtIndexPath ! ) , just use (jsqmessageObj).accessibilityHint to get back your json string decode it and use it as your need.
like - strJson = (jsqmessageObj).accessibilityHint
Hope this will help :p

Cannot assign JSON array or JSON object to a JSON using SwiftyJson in iOS

I have a JSON object:
var x: JSON = JSON([:])
x["1"].intValue = 1 //Correct
x["2"].arrayValue = [JSON("2")] //Error
x["3"].dictionaryValue = [3:JSON("3")] //Error
I am able to assign an integer to x, but I am not able to assign any JSON array or any JSON dictionary. What am I doing wrong and how do I solve it?
Assuming you originally wanted to assign an array of JSON objects:
// doesnt work
x["1"].arrayObject = [ JSON( ["1" : "2"] ), JSON( [ "3" : "4" ] )
Try this:
x["1"] = JSON( [ JSON( ["1" : "2"]), JSON( ["3" : "4"] ) ] )
Or:
x["1"] = [ JSON( ["1" : "2"]).object, JSON( ["3" : "4"] ).object ]
Should work at least for SwiftyJSON 2.3
As of today, the SwiftyJSON README.md tells you to use
json.array = [1,2,3,4]
but actually take a look at SwiftyJSON.swift under MARK: - Array, extension JSON. array and arrayValue are get only, you should instead use
json.arrayObject = [1,2,3,4]
which works if the array elements are native types. If you would like to assign a [JSON] to a JSON, that is, an array of JSON objects to a new JSON object try this
// Patch SwiftyJSON to support constructing a JSON out of [JSON]
extension JSON {
public init(_ jsonArray:[JSON]) {
self.init(jsonArray.map { $0.object })
}
}
and thus
json["subarray"] = JSON(someArrayOfJSON)
Try this:
var x = JSON([:])
x["1"] = 1
x["2"] = [2]
x["3"] = ["3":4]
println(x)

Resources