I'm trying to convert a [String : String] (a Swift Dictionary) to NSDictionary, for later use on a JSON library that produces a string
var parcelDict = ["trackingNumber" : parcel.number,
"dstCountry" : parcel.countryCode];
if (parcel.postalService != nil)
{
parcelDict["postalService"] = parcel.postalService;
}
var errorPtr: NSErrorPointer
let dict: NSDictionary = parcelDict
var data = NSJSONSerialization.dataWithJSONObject(dict, options:0, error: errorPtr) as NSData
return NSString(data: data, encoding: NSUTF8StringEncoding)
but let dict: NSDictionary = parcelDict does not work
let dict: NSDictionary = parcelDict as NSDictionary
var data = NSJSONSerialization.dataWithJSONObject(parcelDict as NSMutableDictionary, options:0, error: errorPtr) as NSData
All of these examples do not work. They produce the following errors:
What's the correct way of doing it?
Update:
Code that works
var parcelDict = ["trackingNumber" : parcel.number!,
"dstCountry" : parcel.countryCode!];
if (parcel.postalService != nil) {
parcelDict["postalService"] = parcel.postalService;
}
var jsonError : NSError?
let dict = parcelDict as NSDictionary
var data = NSJSONSerialization.dataWithJSONObject(dict, options:nil, error: &jsonError)
return NSString(data: data!, encoding: NSUTF8StringEncoding)!
You have to cast it like this:
let dict = parcelDict as NSDictionary
Otherwise the Swift Dictionary and NSDictionary are treated almost the same way when using it in methods for ex:
func test(dict: NSDictionary) {}
let dict = ["Test":1]
test(dict)
Will work completely fine.
After your update
If you change your Dictionary value type to non optional String then your error will go away.
[String:String?] change to -> [String:String]
You can use this easy method
let dictSwift = ["key1": "value1", "key1": value2]
let dictNSMutable = NSMutableDictionary(dictionary: dictSwift)
Enjoy coding!
Don't don't need to convert it to an NSDictionary. Just use:
var data = NSJSONSerialization.dataWithJSONObject(parcelDict, options:0, error: errorPtr) as NSData
It's mentioned in this post: Making a JSON object in Swift
Edit
From your screenshots I believe your problem lies in the value type in your parcelDict Dictionary. I can recreate the error with the following code:
let dict = [String: String?]()
let nsDict = dict as NSDictionary // [String: String?] is not convertible to NSDictionary
However, using a String instead of a String? as the value type removes the error.
Therefore, when populating parcelDict perhaps you should only add to it values which are not nil.
Related
I created an API that has a JSON response in the following format:
[{"name":"xxx","direct_link":"http:\/\/domain.com\/images\/xxx.png","image":"http:\/\/domain.com\/images\/xxx.png"},{"name":"yyy","direct_link":"http:\/\/domain.com\/images\/yyy.png","image":"http:\/\/domain.com\/images\/yyy.png"}]
Notice how the JSON response has no array title.
My Swift code looks like this:
do {
//converting resonse to NSDictionary
var teamJSON: NSDictionary!
teamJSON = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSDictionary
//getting the JSON array teams from the response
let teams: NSArray = teamJSON["teams"] as! NSArray
//looping through all the json objects in the array teams
for i in 0 ..< teams.count{
//getting the data at each index
let teamId:Int = teams[i]["name"] as! String!
let teamName:String = teams[i]["direct_link"] as! String!
let teamMember:Int = teams[i]["image"] as! Int!
//displaying the data
print("name -> ", teamId)
print("direct_link -> ", teamName)
print("image -> ", teamMember)
print("===================")
print("")
}
Notice how the array is looking for a title "teams". How can I ensure the JSON is properly parsed and displays the 3 values I need for the JSON response? I'm new to app coding and have a web background, still trying to wrap my head around this.
As it stands when I try to build and run I get the following error: fatal error unexpectedly found nil while unwrapping an optional value
Try this:
do {
guard let teams = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSArray else {
//Doesn't exist, or isn't an NSArray
return
}
for team in teams {
//getting the data at each index
let teamId = team["name"] as! String
let teamName = team["direct_link"] as! String
let teamMember = team["image"] as! Int
//displaying the data
print("name -> ", teamId)
print("direct_link -> ", teamName)
print("image -> ", teamMember)
print("===================")
print()
}
}
//...
Some notes:
Don't cast to implicitly unwrapped optionals (e.g. String!)
Don't add unnecessary type annotations
Use guard let to enforce preconditions (e.g. the JSON isn't nil, and is castable to NSArray).
Prefer iterating over elements of an Array (for team in teams) over iterating a range (for i in 0..<teams.count)
If you need only the indices, use for i in teams.indices
If you need the indices and elements, use for (index, team) in teams.enumerate()
The problem is this line:
teamJSON = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSDictionary
because you are trying to cast a JSON array to a dictionary.
Just use
teamJSON = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSArray
And then you also won't need to do this:
//getting the JSON array teams from the response
let teams: NSArray = teamJSON["teams"] as! NSArray
Because teamJSON is already the array.
I think the key issue you have to be aware of is that NSJSONSerialization.JSONObjectWithData() can return either an NSDictionary OR an NSArray, depending on whether the root level data in the JSON is an array or a dictionary.
ISSUE:
Your NSJSONSerialization.JSONObjectWithData would return an NSArray and not NSDictionary
SOLUTION:
Update code as below:
var teamJSON = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSArray
//getting the JSON array teams from the response
let teams: NSArray = teamJSON
ALSO
Don't force cast values using !
Always cast it as optional using ?
when decoding JSON from webservice(API) i get error :
Could not cast value of type '__NSDictionaryM' (0x1037ad8a8) to 'NSArray' (0x1037ad470).
My Code :
var kGetURL = "http://bitnami.local/cscart_demo/api/users"
//var kGetURL = "http://localhost/fendy/getjson.php"
var json : Array<AnyObject> = []
override func viewDidLoad() {
super.viewDidLoad()
start()
}
func getData(data : NSData){
//error at this line :
json = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as! Array<AnyObject>
//error
tableView.reloadData()
}
func start(){
var url : NSURL = NSURL(string: kGetURL)!
var data : NSData = NSData(contentsOfURL: url)!
getData(data)
}
if i change url to http://localhost/fendy/getjson.php, its working so nice.
i get error if my url is http://bitnami.local/cscart_demo/api/users
Response from webservice http://localhost/fendy/getjson.php :
[{"id":"17","Name":"KFC","Message":"awesome"},
{"id":"18","Name":"McDonald","Message":"good"},
{"id":"23","Name":"Burger King","Message":"tasty"},
{"id":"38","Name":"Pizza hut","Message":"yummy"},
{"id":"39","Name":"Steak","Message":"very Delicious"}]
Response from webservice http://bitnami.local/cscart_demo/api/users :
{"users":
[{"user_id":"4","user_login":"user_4","is_root":"N","timestamp":"1441608048","user_type":"C","status":"A","firstname":"","lastname":"","email":"fendy.w#mvig.net","company":"","company_id":"1","company_name":"Simtech"},
{"user_id":"3","user_login":"customer","is_root":"N","timestamp":"1441604240","user_type":"C","status":"A","firstname":"Customer","lastname":"Customer","email":"customer#example.com","company":"Simtech","company_id":"1","company_name":"Simtech"},
{"user_id":"1","user_login":"admin","is_root":"Y","timestamp":"1441604240","user_type":"A","status":"A","firstname":"John","lastname":"Doe","email":"robby#mvig.net","company":"Your company","company_id":"0","company_name":null}],
"params":{"page":1,"items_per_page":"10","sort_order":"asc","sort_by":"name","sort_order_rev":"desc","total_items":"3"}}
i think it's Style is same, but why not working with url http://bitnami.local/cscart_demo/api/users . anyone can help?
The bitnami response starts with a { and it is therefore a JSON object, which corresponds to an NSDictionary. The other one starts with [ which indicates an array.
You need to change the type of json to Dictionary<String, AnyObject>, and deserialize as follows:
json = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as! Dictionary<String, AnyObject>
Your method is casting JSON result to an array. It works fine with the URL that returns an array represented as JSON, but it does not work with the URL that returns a dictionary, not an array, represented as JSON.
Although the "style" of returned values looks the same, the second one is a one-item dictionary. What you probably want is to extract the "users" element from it, which is an array.
If you do not know which of the two URLs you are getting, you could try both styles with as? cast instead of as!:
let tmp : AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil)
if let arr = tmp as? Array<AnyObject> {
json = arr
} else if dict = tmp as? [String: AnyObject] {
json = dict["users"] as! Array<AnyObject>
} else {
// Handle an error: the input was unexpected
}
tableView.reloadData()
try cache for Serialization
do {
if let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String : Any] { // as? data type
if let otherDict = json["dataKey"] as? [String : Any] {
//do something
}
}
} catch {
// can't json serialization
}
I'd like to use JSON output from the public Web Service https://api.rbp-zp.cz/1.3/services/district/ but looks like there is a problem with encoding in this case.
AFNetworking use to do conversion in such cases (by specific respondSerializer) like this:
var op = AFHTTPRequestOperation (request: request)
op.responseSerializer = AFJSONResponseSerializer()
but it fails because of the invalid JSON structure.
I have tried to do conversion myself, but no outcome
let rObject: AnyObject? = NSJSONSerialization.JSONObjectWithData(decodedData, options:.AllowFragments, error: nil)
When I put manually this JSON from browser to validator http://jsonlint.com, then it says it is in incorrect format.
Strangely enough when I put the JSON message from my log into the validator then the structure is valid.
Any help would be appreciated.
func convertStringToDictionary(text: String) -> [String:String]? {
if let data = text.dataUsingEncoding(NSUTF8StringEncoding) {
var error: NSError?
let json = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.allZeros, error: &error) as? [String:String]
if error != nil {
println(error)
}
return json
}
return nil
}
let str = "{\"name\":\"James\"}"
let result = convertStringToDictionary(str) // ["name": "James"]
if let name = result?["name"] { // The `?` is here because our `convertStringToDictionary` function returns an Optional
println(name) // "James"
}
In your version, you didn't pass the proper parameters to NSJSONSerialization and forgot to cast the result. Also, it's better to check for the possible error. Last note: this works only if your value is a String. If it could be another type, it would be better to declare the dictionary conversion like this:
let json = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.allZeros, error: &error) as? [String:AnyObject]
and of course you would also need to change the return type of the function:
func convertStringToDictionary(text: String) -> [String:AnyObject]? { ... }
Ok, so workaround is trimming those funny characters in the end,..don't like it much but works
let range:NSRange = NSMakeRange(0, res.length - 3);
let refinedData:NSData = res.subdataWithRange(range)
let resObject = NSJSONSerialization.JSONObjectWithData(refinedData, options:NSJSONReadingOptions.allZeros, error: &error) as! NSArray
resObject is correctly filled as NSArray now
I want to parse a json file, this is in my json file:
{
"currentPowerByClient": 0, <- i want to read this
"currentPowerToClient":518,
"tariff":1,
"totalGasDelivered":1061.004,
"totalPowerByClientHigh":10.704,
"totalPowerByClientLow":23.042,
"totalPowerToClientHigh":912.221,
"totalPowerToClientLow": 693.499
}
this is my swift code, the object called JSONResult contains my JSON code
var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options:
NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
if let item = jsonResult as NSDictionary? {
if let currentPowerByClient = item["currentPowerByClient"] as? NSDictionary {
println(currentPowerByClient)
}
}
when i run it, it doesn't print anything
The line
if let currentPowerByClient = item["currentPowerByClient"] as? NSDictionary
should be
if let currentPowerByClient = item["currentPowerByClient"] as? NSNumber
because item["currentPowerByClient"] is expected to be a number, not a dictionary. it works then
I tried to get the value of "seed" from json response. But i am getting nil.
Json Response:
{
"response": {
"params": {
"rows": "20",
"defType": "abc",
"seed": "381786611"
}
}
}
Swift Parsing:
if let responseHeader:AnyObject = object?["response"] as? NSDictionary {
if let t = (responseHeader["params"] as? NSDictionary){
let t1 = t["seed"] as? String
println("result is \(t1)") // This returns nil
}
}
Json Parsing
func processJsonToDictionary(object:AnyObject?) -> AnyObject?{
if object != nil {
if let data: AnyObject = object {
var parseError: NSError?
var jsonResult = NSJSONSerialization.JSONObjectWithData(object as NSData!, options: NSJSONReadingOptions.MutableContainers, error: &parseError) as? NSDictionary
if(parseError != nil){
return parseError
}
else{
return jsonResult
}
}
}
return nil
}
I am not able to get the value of t1. it always returns nil.
How can i get the value.
Also, I put a breakpoint and tried to print the value of t1. But the Xcode Keeps crashing. Why?
I think the major problem here is only accessing a JSON object in swift.
var error: NSError?
let jsonDict = NSJSONSerialization.JSONObjectWithData(jsonData, options: nil, error: &error) as NSDictionary
let resp = jsonDict["response"] as? NSDictionary
let params = resp?["params"]?["seed"]
let seed = params!! as NSString
This is just to show you how a JSON object is accessed in swift. You can ofcourse change it according to your needs to remove unwanted Optional Chaining.
For easy JSON manipulation in Swift you could try this little library. It seems pretty easy and you could do this:
var dictionary: [String: AnyObject]!
if let json = NKJSON.parse(yourNSDataObject) {
dictionary <> json[NKJSON.rootKey]
}
First confirm that the response which you are getting is in json format or in string format.
For json parsing you can use swifty json pod