Convert JSON String to Swift Dictionary - swift

I'm trying to convert a json string to a dictionary.
I came across this answer but it only works for arrays with one value.
Here is an example of a string I'm trying to turn into a swift dictionary
[
{"value1":"Reporting for duty"},
{"value2":"Post received"},
{"value3":"frogg222"},
{"value4":"Still reporting"}
]
Just to be clear, the following code does not work and will return nil with the above string.
func convertStringToDictionary(text: String) -> [String:AnyObject]? {
if let data = text.data(using: String.Encoding.utf8) {
do {
return try JSONSerialization.jsonObject(with: data, options: []) as? [String:AnyObject]
} catch let error as NSError {
print(error)
}
}
return nil
}

Related

Json array Dictionary parse nil in swift 5

I have a json like bellow ..
{"type": "Polygon","coordinates":[[[90.40082675305842,23.708825220302813],[90.4018551231959,23.708188760430843],[90.40247361862504,23.7091957460091],[90.40143983815886,23.70975584674032],[90.40082675305842,23.708825220302813]]]}
I have used bellow code to parse which returns nil
let json = myjsonString as? [String: Any]
But it returns nil. please help me to parse above json
You have to deserialize the string, either with JSONSerialization or – more comfortable – with JSONDecoder
let jsonString = """
{"type": "Polygon","coordinates":[[[90.40082675305842,23.708825220302813],[90.4018551231959,23.708188760430843],[90.40247361862504,23.7091957460091],[90.40143983815886,23.70975584674032],[90.40082675305842,23.708825220302813]]]}
"""
struct Overlay : Decodable {
let type : String
let coordinates : [[[Double]]]
}
do {
let result = try JSONDecoder().decode(Overlay.self, from: Data(jsonString.utf8))
print(result)
} catch {
print(error)
}
At first, you need to convert data from your input string then you can convert data to dictionary object. You can try this example.
let jsonText = "{\"first_name\":\"Sergey\"}"
var dictonary:NSDictionary?
if let data = jsonText.data(using: String.Encoding.utf8) {
do {
dictonary = try JSONSerialization.jsonObject(with: data, options: []) as? [String:AnyObject]
if let myDictionary = dictonary {
print(" First name is: \(myDictionary["first_name"]!)")
}
} catch let error as NSError {
print(error)
}
}

Serializing JSON ReadingOptions with inconsistent output in Swift 4.2

I'm working on a pet project where I am serializing JSON using the JSONSerialization class and jsonObject(with:options:). The object is unusable until cast into a Dictionary [String: Any] or an Array [Any]. This is where the inconsistency occurs. The following is a method from one of my classes. The input is tested and valid.
private static func parse(data: Data) -> [JSONDictionary]? {
do {
let options = JSONSerialization.ReadingOptions() // rawValue = UInt 0
let otherOptions: JSONSerialization.ReadingOptions = [] // rawValue = UInt 0
let jsonAny = try JSONSerialization.jsonObject(with: data, options: otherOptions)
if let array = jsonAny as? [String: Any] {
print(array)
}
} catch {
return nil
}
return nil
}
Both of the ReadingOption objects are valid and produce valid output that can be properly cast, and print(array) is called.
However, when I use the following, invalid output is returned and can not be cast properly. Note options in the jsonObject call has an equivalent value to otherOptions in the above example.
private static func parse(data: Data) -> [JSONDictionary]? {
do {
let jsonAny = try JSONSerialization.jsonObject(with: data, options: [])
if let array = jsonAny as? [String: Any] {
print(array) // never called
}
} catch {
return nil
}
return nil
}
I thought because they have equivalent values that I could use them in place of each other. But that is not the case. Is this a bug, or am I using this incorrectly?
Edit: here is the dataset being used https://www.govtrack.us/api/v2/role?current=true&role_type=senator
The reading options are irrelevant. In Swift ReadingOptions are only useful if the expected result is not array or dictionary.
If the expected type is array or dictionary omit the options parameter.
The inconsistency is that your return type is an array ([JSONDictionary]) but the actual type is a dictionary.
private static func parse(data: Data) -> JSONDictionary? {
do {
let jsonAny = try JSONSerialization.jsonObject(with: data)
if let jsonDictionary = jsonAny as? JSONDictionary {
return jsonDictionary
}
} catch {
print(error)
return nil
}
return nil
}
It's recommended to hand over an error of a throwing method
enum SerializationError : Error {
case typeMismatch
}
private static func parse(data: Data) throws -> JSONDictionary {
let jsonAny = try JSONSerialization.jsonObject(with: data)
guard let jsonDictionary = jsonAny as? JSONDictionary else { throw SerializationError.typeMismatch }
return jsonDictionary
}

Dictionary extension in Swift is not recognized

I am trying to extend Dictionary with the following code:
extension Dictionary where Key: ExpressibleByStringLiteral, Value: AnyObject {
var jsonString: String? {
if let dict = (self as AnyObject) as? Dictionary<String, AnyObject> {
do {
let data = try JSONSerialization.data(withJSONObject: dict, options: JSONSerialization.WritingOptions(rawValue: UInt.allZeros))
if let string = String(data: data, encoding: String.Encoding.utf8) {
return string
}
} catch {
print(error)
}
}
return nil
}
}
Where I write something like:
let x: [String: String] = ["": ""]
x.jsonString
I get this error:
Value of type '[String: String]' as no member 'jsonString'
Anything I am missing?
There is no need to constrain Dictionary Value type at all:
extension Dictionary where Key: ExpressibleByStringLiteral {
var jsonString: String? {
guard let data = try? JSONSerialization.data(withJSONObject: self)
else { return nil }
return String(data: data, encoding: .utf8)
}
}
Since String is a value type , look for it's
public struct String {
and AnyObject refers to any instance of a class only , and is equivalent to id in Objective-C , so this declaration of x
[String: String] doesn't fit with [String: AnyObject]
because Any refers to any instance of a class, struct, or enum so it'll fit perfectly

Getting nil value when read JSON data in Swift

if let postString = NSString(data:data!, encoding: NSUTF8StringEncoding) as? String {
guard let jsonData = postString.dataUsingEncoding(NSASCIIStringEncoding) else {
fatalError()
}
guard let jsonObjects = try? NSJSONSerialization.JSONObjectWithData(jsonData,options: [])
,let JSONArray = jsonObjects as? [[String: AnyObject]]
else {
fatalError()
}
print(JSONArray)
}
In postString constant, I am getting "[{\"Name\":\"ABC\",\"Age\":35},{\"Name\":\"CDE\",\"Age\":36‌​}]"
and when I run this code then fatalError() code call.
A couple of other people have explained what you did wrong in this case (Tried to cast the output of deserialization to a dictionary when it actually contains an array.)
Stepping back from the details, when something fails, you need to break your code into smaller pieces and then trace through it to see what's failing.
The "as?" cast says "Try to cast this object to another type. Wrap the results in an Optional." If the cast fails, the result is nil. If it succeeds, the optional contains the new type.
If you rewrote your code as:
let jsonObject = try NSJSONSerialization.JSONObjectWithData(data!,
options: NSJSONReadingOptions.AllowFragments)
guard let jsonDict = jsonObject as [String: Any] else {
print("Cast failed")
return
}
Then the print statement would fire and you'd know that the cast was the problem.
EDIT:
I just noticed that your JSON data contains an array of dictionaries of type [String: Int]. In Swift Ints are not an Object type, so you need to cast your results to [[String:Any]], not [[String:AnyObject]]. I've fixed my code above.
I Wrote the following code in a playground and it works:
let jsonString = "[{\"Name\":\"ABC\",\"Age\":35},{\"surveyName\":\"CDE\",\"Age\":36}]"
guard let jsonData = jsonString.data(using: .ascii) else {
fatalError()
}
guard let jsonObjects = try? JSONSerialization.jsonObject(with: jsonData, options: []),
let JSONArray = jsonObjects as? [[String: Any]]
else {
fatalError()
}
print(String(describing: jsonObjects))
It gives the output:
(
{
Age = 35;
Name = ABC;
},
{
Age = 36;
surveyName = CDE;
}
)
Which is what I would expect.
EDIT #2:
Actually, on further investigation I'm stumped as to why your code isn't working. I just tested it, and the as [[String: AnyObject]] works. It turns out that in Swift 3 if you cast an Int to AnyObject and you've included Foundation (or UIKit) then it gets silently converted to an NSNumber, which IS an Object type.
You're going to need to show your actual JSON data and the code that converts it to an object if you need help debugging it.
EDIT #3:
Below is code I wrote and tested in Swift 2.3:
func parseJSONTest() {
let jsonString = "[{\"Name\":\"ABC\",\"Age\":35},{\"surveyName\":\"CDE\",\"Age\":36}]"
guard let jsonData = jsonString.dataUsingEncoding(NSASCIIStringEncoding) else {
fatalError()
}
//I'm not sure why you take JSON data, convert it to a string, and convert
//It back to NSData, but to prove a point, this is your code being fed
//well-formed JSON data in an NSData object:
if let postString = NSString(data:jsonData,
encoding: NSASCIIStringEncoding) as? String {
guard let jsonData = postString.dataUsingEncoding(NSASCIIStringEncoding) else {
fatalError()
}
guard let jsonObjects = try? NSJSONSerialization.JSONObjectWithData(jsonData,options: []),
let JSONArray = jsonObjects as? [[String: AnyObject]]
else {
fatalError()
}
print(JSONArray)
}
}
In order to provide a complete test I first take a string containing JSON data and convert it to NSData. I then convert that NSData back to JSON objects and cast them to the desired type, and it works. The code above displays:
[["Name": ABC, "Age": 35], ["surveyName": CDE, "Age": 36]]
Which matches the structure you have (an array of dictionaries).
The above is technically not an json object it is a json array.
Try casting as [AnyObject] instead of [String: AnyObject]
Then use the array to access the item you need. Then cast that to
[String:AnyObject]
because they are json objects within a json array

Downcast from 'String?!' to 'String' only unwraps optionals; did you mean to use '!!'? in swift

source-code are bellow
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments)
if let blogs = json["profile_image_url"] as? String {
userImage = blogs//json["profile_image_url"] as! String
print("USER IMAGE:\(userImage)")
how i solve this issue
You want to test and unwrap any Optional before you use them. This includes casts like as?. If you can avoid it you should not use forced-unwrapping or explicitly-unwrapped Optional (marked with a !) because they lead to unexpected runtime crashes.
import Foundation
// create test data
let testJson = ["profile_image_url": "http://some.site.com/"]
var data: NSData?
// convert to NSData as JSON
do {
data = try NSJSONSerialization.dataWithJSONObject(testJson, options: [])
} catch let error as NSError {
print(error)
}
// decode NSData
do {
// test and unwrap data
if let data = data {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments)
// test and unwrap cast to String
if let userImage = json["profile_image_url"] as? String {
print("USER IMAGE:\(userImage)")
}
}
} catch let error as NSError {
print(error)
}