Swifty Json parsing - ios

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

Related

Unable to extract data properly from JSON in swift

I have a this kind of json object in my response after parsing json string to object
[
"requestId": 1,
"response": {
code = SUCCESS;
},
"messageId": ACTION_COMPLETE
]
I am trying to extract requestId using
responseMsg["requestId"] as! Int
I am getting this error
Could not cast value of type 'NSTaggedPointerString' (0x21877a910) to
'NSNumber' (0x218788588).
I tried it changing to Int(responseMsg["requestId"] as! String)!
This thing is working for positive numbers but not for negative numbers probably bcz when requestId = -2 it throws me an error
Could not cast value of type '__NSCFNumber' (0x21877a000) to
'NSString' (0x218788290).
I tried with different other solution too but did not work.
For parsing the JSON data, its better use Codable instead of manually parsing everything.
For JSON format,
{
"requestId": 1,
"response": {
"code":"SUCCESS"
},
"messageId": "ACTION_COMPLETE"
}
Create Models like,
struct Root: Decodable {
let requestId: String?
let messageId: String
let response: Response
enum CodingKeys: String, CodingKey {
case requestId, messageId, response
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
if let id = try? values.decode(Int.self, forKey: .requestId) {
requestId = String(id)
} else if let id = try? values.decode(String.self, forKey: .requestId) {
requestId = id
} else {
requestId = nil
}
messageId = try values.decode(String.self, forKey: .messageId)
response = try values.decode(Response.self, forKey: .response)
}
}
Now, parse the JSON data using,
do {
let root = try JSONDecoder().decode(Root.self, from: data)
print(root.requestId) //access requestId like this....
} catch {
print(error)
}
Try
Int(String(describing: responseMsg["requestId"]))!
This ensures any data is converted to string first and then to int
This error message
Could not cast value of type 'NSTaggedPointerString' (0x21877a910) to 'NSNumber' (0x218788588).
tells us that the JSON request id is being parsed as a string. NSTaggedPointerString is a special internal type used by the ObjC runtime to represent strings.
Try this:
let requestId = responseMsg["requestId"] as! String
print("request id: \(requestId)") // Prints a string
Note, it may print something that looks like a number, but it isn't one.
The JSON you are parsing probably looks like
{
"requestId": "1",
"response": {
"code" = "SUCCESS"
},
"messageId": "ACTION_COMPLETE"
}
Note the 1 in quotes.
Swift 5
String interpolation is what worked for me! (casting it first to String didn't work as I had other values for which the json decoder actually did its job and cast them directly to a number)
if let responseMsg_any = responseMsg["requestId"],
let responseMsg_int = Int("\(responseMsg_any)") {
//..
}
Warning:
This solution allows any Type to become a String and be checked for a Int value. Only use this if you're not concerned about the value's Type prior to interpolation.

How Fix Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 52."

How to Convert this one. "{\n ID = \"d9a7c7bf-781d-47b3-bb4e-e1022ec4ce1b\";\n Name = Headquarters;\n}"; To this format {
"ID": "d9a7c7bf-781d-47b3-bb4e-e1022ec4ce1b",
"Name": "Headquarters"
}
if let jsonString = text as? String {
let objectData = jsonString.data(using: String.Encoding.utf8)
do {
let json = try JSONSerialization.jsonObject(with: objectData!, options: .allowFragments) as! [String:Any] //try JSONSerialization.jsonObject(with: objectData!, options: JSONSerialization.ReadingOptions.mutableContainers)
print(String(describing: json))
return json
} catch {
// Handle error
print(error)
}
}
Blockquote
First of all and already mentioned the string format is clearly not JSON.
It's the string format which is returned when calling the description property of a Foundation collection type (NSArray / NSDictionary).
For example a print statement calls description and this format appears also in output of Terminal.app.
However there is a solution: This string format is called openStep (an OpenStep / NeXt legacy format) and is available in PropertyListSerialization
This code reads the format:
let string = "{\n ID = \"d9a7c7bf-781d-47b3-bb4e-e1022ec4ce1b\";\n Name = Headquarters;\n}"
let data = Data(string.utf8)
do {
let dictionary = try PropertyListSerialization.propertyList(from: data, format: nil)
print(dictionary)
} catch { print(error) }
Note:
I'm pretty sure that the original data format is not openStep and somewhere you created the string unnecessarily with the String(describing initializer like in the question.
your json format is incorrect. If you try it with jsonformatter it will throw this error:
so first you need to replace ; with ,. The second is that Strings should be wrapped in double quotes, replace Name = Headquarters with Name = "Headquarters".
This is the right form
{\n ID = \"d9a7c7bf-781d-47b3-bb4e-e1022ec4ce1b\",
\n Name = "Headquarters"\n}

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

how to parse the json using alamofire in swift 2?

I am having trouble with parsing json using alamofire (it is working fine using browser or postman),below is my code
Alamofire.request(.POST, reqStr.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())! , parameters: nil).responseJSON { response in
GMDCircleLoader.hideFromView(self.view, animated: false)
if let dict = response.result.value {
self.selectArray = dict as! NSArray as! [NSDictionary]
if self.selectArray.count == 0 {
}else{
self.selectProperty.hidden = false
}
}
}
i am getting below error Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 14." UserInfo={NSDebugDescription=Invalid value around character 14.}
and searched a lot some body suggested that use responseString instead of responseJSON, if i use responseString i am getting data but in string format like
"{"Properties":[{ "ID":"22", "post_title":"Test Property" },{ "ID":"24", "post_title":"TestProperty" }]}"
how can convert it to Dictionary i used below code
if let dict = response.result.value
self.resulArray = dict as! [NSDictionary];
BUt it is crashing.
1)is their any way to get the results using responseJSON.
2) if i use responseString how to get convert to dictinary.
thanks in advance , I am new in Swift i am using swift2.0.

SwiftyJSON keeps returning empty objects

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
}

Resources