How to append JSON values into array using Swift 4? - ios

I am trying to get JSON values and appending into array. Here, below code add_user_product have a chance to come null. If it is null need to append null into array and if not null need to store ID also.
I am trying to get output like - [10,null,12,13,null,……]
// add_user_products & If add_user_product == null need to store null otherwise add_user_product ["id"]
if let add_user_product = fields[“add_user_product"] as? [String : Any] {
let add_id = add_user_product["id"] as! Int
self.addlistIDData.append(add_id)
}
else {
//print("Failure")
}
below my sample response
{
"students":[
{
"grade":0,
"add_user_product":
{
"id":10
}
},
{
"grade":1,
"add_user_product":null
},
{
"grade":2,
"add_user_product":
{
"id":11
}
}
]
}
Expected output: [10,null,11,......] //This array I am going to use Tableview cell

I suggest use nil instead of null string.
Declare your addlistIDData type as [Int?] where Int is an Optional.
Consider below example I have created for you:
var addlistIDData: [Int?] = [10, nil, 12, 13, nil] //Created array just for example
//created dict for testing purpose
let fields: [String : Any]? = ["add_user_product": ["id": nil]]
if let field = fields {
if let add_user_product = field["add_user_product"] as? [String:Any] {
let add_id = add_user_product["id"] as? Int
//now append your object here weather it's a nil or it has any value
addlistIDData.append(add_id)
}
}
else {
//print("Failure")
}
print(addlistIDData)
And output will be:
[Optional(10), nil, Optional(12), Optional(13), nil, nil]
PS: You need to cast an object with if let or with guard let whenever you are accessing objects from this addlistIDData array.

null will not be identifiable, the only way to store it in your array would be to store it as String, But for that also you'll have to store othere elements as String.
But i would suggest instead of adding null just add 0 as:
var arr = [Int]()
if let add_user_product = fields["add_user_product"] as? [String: Any] {
if let productId = add_user_product["id"] as? Int {
arr.append(productId)
} else {
arr.append(0)
}
} else {
//
}

You can do like this:
var resultArray = [Int?]()
if let add_user_product = fields["add_user_product"] as? [String: Any] {
if let add_id = add_user_product["id"] as? Int {
resultArray.append(add_id)
} else {
resultArray.append(nil)
}
} else {
//print("Failure")
}
Hope this Helps.

You can use compactMap:
let arrayDict = [ ["id" : 3], ["id" : nil], ["id" : 5] ]
let result = arrayDict.compactMap { $0["id"] }
print(result)
Output:
[Optional(3), nil, Optional(5)]

Related

What is the proper way to use EVReflection to parse a Dictionary type?

I am using EVReflection in my app. One JSON response should be parsed as type Dictionary<String,Array<MyObject>>. I have successfully parsed this by overriding the setValue method like this:
override func setValue(_ value: Any!, forUndefinedKey key: String) {
switch key {
case "response":
if let dict = value as? NSDictionary {
response = Dictionary<String,Array<MyObject>>();
for (key, value) in dict {
var listValues = Array<MyObject>();
if let array = value as? NSArray {
for vd in array {
listValues.append(MyObject(dictionary: vd as! NSDictionary));
}
}
response![key as? String ?? ""] = listValues;
}
}
break;
}
}
However, I am seeing the following error in the console:
ERROR: Could not create an instance for type Swift.Dictionary<Swift.String, Swift.Array<MyObject>>
Is there a different way I should be doing this? How do I get the error to go away?
I was able to figure this out by using a propertyConverter as follows:
override public func propertyConverters() -> [(key: String, decodeConverter: ((Any?) -> ()), encodeConverter: (() -> Any?))] {
return[
(
key: "response"
, decodeConverter: {
if let dict = $0 as? NSDictionary {
self.response = Dictionary<String,Array<MyObject>>();
for (key, value) in dict {
var listValues = Array<MyObject>();
if let array = value as? NSArray {
for vd in array {
listValues.append(MyObject(dictionary: vd as! NSDictionary));
}
}
self.response![key as? String ?? ""] = listValues;
}
}
}
, encodeConverter: { return nil }
)
]
}
With EVReflection you should be using NSDictionary not a Dictionary (which is a struct).
If you do this then you shouldn't need to override any property converter methods.

Turn swift object into a JSON string

I have classes like these:
class MyDate
{
var year : String = ""
var month : String = ""
var day : String = ""
init(year : String , month : String , day : String) {
self.year = year
self.month = month
self.day = day
}
}
class Lad
{
var firstName : String = ""
var lastName : String = ""
var dateOfBirth : MyDate?
init(firstname : String , lastname : String , dateofbirth : MyDate) {
self.firstName = firstname
self.lastName = lastname
self.dateOfBirth = dateofbirth
}
}
class MainCon {
func sendData() {
let myDate = MyDate(year: "1901", month: "4", day: "30")
let obj = Lad(firstname: "Markoff", lastname: "Chaney", dateofbirth: myDate)
let api = ApiService()
api.postDataToTheServer(led: obj)
}
}
class ApiService {
func postDataToTheServer(led : Lad) {
// here i need to json
}
}
And I would like to turn a Lad object into a JSON string like this:
{
"firstName":"Markoff",
"lastName":"Chaney",
"dateOfBirth":
{
"year":"1901",
"month":"4",
"day":"30"
}
}
EDIT - 10/31/2017: This answer mostly applies to Swift 3 and possibly earlier versions. As of late 2017, we now have Swift 4 and you should be using the Encodable and Decodable protocols to convert data between representations including JSON and file encodings. (You can add the Codable protocol to use both encoding and decoding)
The usual solution for working with JSON in Swift is to use dictionaries. So you could do:
extension Date {
var dataDictionary {
return [
"year": self.year,
"month": self.month,
"day": self.day
];
}
}
extension Lad {
var dataDictionary {
return [
"firstName": self.firstName,
"lastName": self.lastName,
"dateOfBirth": self.dateOfBirth.dataDictionary
];
}
}
and then serialize the dictionary-formatted data using JSONSerialization.
//someLad is a Lad object
do {
// encoding dictionary data to JSON
let jsonData = try JSONSerialization.data(withJSONObject: someLad.dataDictionary,
options: .prettyPrinted)
// decoding JSON to Swift object
let decoded = try JSONSerialization.jsonObject(with: jsonData, options: [])
// after decoding, "decoded" is of type `Any?`, so it can't be used
// we must check for nil and cast it to the right type
if let dataFromJSON = decoded as? [String: Any] {
// use dataFromJSON
}
} catch {
// handle conversion errors
}
If you just need to do this for few classes, providing methods to turn them into dictionaries is the most readable option and won't make your app noticeably larger.
However, if you need to turn a lot of different classes into JSON it would be tedious to write out how to turn each class into a dictionary. So it would be useful to use some sort of reflection API in order to be able to list out the properties of an object. The most stable option seems to be EVReflection. Using EVReflection, for each class we want to turn into json we can do:
extension SomeClass: EVReflectable { }
let someObject: SomeClass = SomeClass();
let someObjectDictionary = someObject.toDictionary();
and then, just like before, we can serialize the dictionary we just obtained to JSON using JSONSerialization. We'll just need to use object.toDictionary() instead of object.dataDictionary.
If you don't want to use EVReflection, you can implement reflection (the ability to see which fields an object has and iterate over them) yourself by using the Mirror class. There's an explanation of how to use Mirror for this purpose here.
So, having defined either a .dataDictionary computed variable or using EVReflection's .toDictionary() method, we can do
class ApiService {
func postDataToTheServer(lad: Lad) {
//if using a custom method
let dict = lad.dataDictionary
//if using EVReflection
let dict = lad.toDictionary()
//now, we turn it into JSON
do {
let jsonData = try JSONSerialization.data(withJSONObject: dict,
options: .prettyPrinted)
// send jsonData to server
} catch {
// handle errors
}
}
}
May this GitHub code will help you.
protocol SwiftJsonMappable {
func getDictionary() -> [String: Any]
func JSONString() -> String
}
extension SwiftJsonMappable {
//Convert the Swift dictionary to JSON String
func JSONString() -> String {
do {
let jsonData = try JSONSerialization.data(withJSONObject: self.getDictionary(), options: .prettyPrinted)
// here "jsonData" is the dictionary encoded in JSON data
let jsonString = String(data: jsonData, encoding: .utf8) ?? ""
// here "decoded" is of type `Any`, decoded from JSON data
return jsonString
// you can now cast it with the right type
} catch {
print(error.localizedDescription)
}
return ""
}
//Convert Swift object to Swift Dictionary
func getDictionary() -> [String: Any] {
var request : [String : Any] = [:]
let mirror = Mirror(reflecting: self)
for child in mirror.children {
if let lable = child.label {
//For Nil value found for any swift propery, that property should be skipped. if you wanna print nil on json, disable the below condition
if !checkAnyContainsNil(object: child.value) {
//Check whether is custom swift class
if isCustomType(object: child.value) {
//Checking whether its an array of custom objects
if isArrayType(object: child.value) {
if let objects = child.value as? [AMSwiftBase] {
var decodeObjects : [[String:Any]] = []
for object in objects {
//If its a custom object, nested conversion of swift object to Swift Dictionary
decodeObjects.append(object.getDictionary())
}
request[lable] = decodeObjects
}
}else {
//Not an arry, and custom swift object, convert it to swift Dictionary
request[lable] = (child.value as! AMSwiftBase).getDictionary()
}
}else {
request[lable] = child.value
}
}
}
}
return request
}
//Checking the swift object is swift base type or custom Swift object
private func isCustomType(object : Any) -> Bool {
let typeString = String(describing: type(of: object))
if typeString.contains("String") || typeString.contains("Double") || typeString.contains("Bool") {
return false
}
return true
}
//checking array
private func isArrayType(object : Any) -> Bool {
let typeString = String(describing: type(of: object))
if typeString.contains("Array"){
return true
}
return false
}
//Checking nil object
private func checkAnyContainsNil(object : Any) -> Bool {
let value = "\(object)"
if value == "nil" {
return true
}
return false
}
}
https://github.com/anumothuR/SwifttoJson

How to detect an empty Dictionary without crashing in Swift 2.0?

I am trying to detect if the dictionary coming from API is empty or has values but whenever i am trying to do Dict.count it crashes.
if let personalInfo = self.scanResult?.fields { // personalInfo has 0 values but not nil
let image = NSData(base64EncodedString: (personalInfo["Photo"] as? String)!, options: NSDataBase64DecodingOptions(rawValue: 0)) // Crashes
}
I also tried isEmpty and it crashes there as well.
You should unwrap personalInfo["Photo"] before trying to use it. This way you can ensure you're not trying to instantiate NSData without a value for the Photo key being present
if let personalInfo = self.scanResult?.fields{
if let encodedString = personalInfo["Photo"] as? String{
let image = NSData(base64EncodedString: encodedString,options: NSDataBase64DecodingOptions(rawValue:0))
}
}
I think the simplest solution is to check dictionary keys count is greater than 0. For example:
let dictionary: Dictionary<String, AnyObject> = Dictionary()
if dictionary.keys.count > 0 {
// This mean dictionary has values.
}
The problem was that the self.scanResult.fields was coming out as NSNull. so i found these 3 methods which resolves the problem.
func isNotNull(object:AnyObject?) -> Bool {
guard let object = object else {
return false
}
return (isNotNSNull(object) && isNotStringNull(object))
}
func isNotNSNull(object:AnyObject) -> Bool {
return object.classForCoder != NSNull.classForCoder()
}
func isNotStringNull(object:AnyObject) -> Bool {
if let object = object as? String where object.uppercaseString == "NULL" {
return false
}
return true
}
And i just call like this :
if self.isNotNSNull((self.scanResult?.fields)!
{
}
You can use the "count" of dictionary. See the code.
if let personalInfo = self.scanResult?.fields {
if personalInfo.count > 0 {
if let base64ImageString = personalInfo["Photo"] as? String {
if let image = NSData(base64EncodedString: base64ImageString, options: NSDataBase64DecodingOptions(rawValue: 0)) {
// do your stuff with image
}
}
}
}

Dynamically remove null value from swift dictionary using function

I have following code for dictionary
var dic : [String: AnyObject] = ["FirstName": "Anvar", "LastName": "Azizov", "Website": NSNull(),"About": NSNull()]
I already remove key which have null value using below code
var keys = dic.keys.array.filter({dic[$0] is NSNull})
for key in keys {
dic.removeValueForKey(key)
}
It works for static dictionary,But I want do it dynamically,I want to done it using function but whenever I pass dictionary as a argument it works as a let means constant so can not remove null key
I make below code for that
func nullKeyRemoval(dic : [String: AnyObject]) -> [String: AnyObject]{
var keysToRemove = dic.keys.array.filter({dic[$0] is NSNull})
for key in keysToRemove {
dic.removeValueForKey(key)
}
return dic
}
please tell me solution for this
Rather than using a global function (or a method), why not making it a method of Dictionary, using an extension?
extension Dictionary {
func nullKeyRemoval() -> Dictionary {
var dict = self
let keysToRemove = Array(dict.keys).filter { dict[$0] is NSNull }
for key in keysToRemove {
dict.removeValue(forKey: key)
}
return dict
}
}
It works with any generic types (so not limited to String, AnyObject), and you can invoke it directly from the dictionary itself:
var dic : [String: AnyObject] = ["FirstName": "Anvar", "LastName": "Azizov", "Website": NSNull(),"About": NSNull()]
let dicWithoutNulls = dic.nullKeyRemoval()
Swift 5 adds compactMapValues(_:), which would let you do
let filteredDict = dict.compactMapValues { $0 is NSNull ? nil : $0 }
For Swift 3.0 / 3.1 this could be helpful. Also removes NSNull objects recursive:
extension Dictionary {
func nullKeyRemoval() -> [AnyHashable: Any] {
var dict: [AnyHashable: Any] = self
let keysToRemove = dict.keys.filter { dict[$0] is NSNull }
let keysToCheck = dict.keys.filter({ dict[$0] is Dictionary })
for key in keysToRemove {
dict.removeValue(forKey: key)
}
for key in keysToCheck {
if let valueDict = dict[key] as? [AnyHashable: Any] {
dict.updateValue(valueDict.nullKeyRemoval(), forKey: key)
}
}
return dict
}
}
Swift 3+: Remove null from dictionary
func removeNSNull(from dict: [String: Any]) -> [String: Any] {
var mutableDict = dict
let keysWithEmptString = dict.filter { $0.1 is NSNull }.map { $0.0 }
for key in keysWithEmptString {
mutableDict[key] = ""
}
return mutableDict
}
Use:
let outputDict = removeNSNull(from: ["name": "Foo", "address": NSNull(), "id": "12"])
Output: ["name": "Foo", "address": "", "id": "12"]
Nested NSNull supported
To remove any NSNull appearance in any nested level (including arrays and dictionaries), try this:
extension Dictionary where Key == String {
func removeNullsFromDictionary() -> Self {
var destination = Self()
for key in self.keys {
guard !(self[key] is NSNull) else { destination[key] = nil; continue }
guard !(self[key] is Self) else { destination[key] = (self[key] as! Self).removeNullsFromDictionary() as? Value; continue }
guard self[key] is [Value] else { destination[key] = self[key]; continue }
let orgArray = self[key] as! [Value]
var destArray: [Value] = []
for item in orgArray {
guard let this = item as? Self else { destArray.append(item); continue }
destArray.append(this.removeNullsFromDictionary() as! Value)
}
destination[key] = destArray as? Value
}
return destination
}
}
Swift 4
A little more efficient than the other solutions. Uses only O(n) complexity.
extension Dictionary where Key == String, Value == Any? {
var trimmingNullValues: [String: Any] {
var copy = self
forEach { (key, value) in
if value == nil {
copy.removeValue(forKey: key)
}
}
return copy as [Key: ImplicitlyUnwrappedOptional<Value>]
}
}
Usage: ["ok": nil, "now": "k", "foo": nil].trimmingNullValues // =
["now": "k"]
If your dictionary is mutable you could do this in place and prevent the inefficient copying:
extension Dictionary where Key == String, Value == Any? {
mutating func trimNullValues() {
forEach { (key, value) in
if value == nil {
removeValue(forKey: key)
}
}
}
}
Usage:
var dict: [String: Any?] = ["ok": nil, "now": "k", "foo": nil]
dict.trimNullValues() // dict now: = ["now": "k"]
The cleanest way to do it, just 1 line
extension Dictionary {
func filterNil() -> Dictionary {
return self.filter { !($0.value is NSNull) }
}
}
Rather than using a global function (or a method), why not making it a method of Dictionary, using an extension?
extension NSDictionary
{
func RemoveNullValueFromDic()-> NSDictionary
{
let mutableDictionary:NSMutableDictionary = NSMutableDictionary(dictionary: self)
for key in mutableDictionary.allKeys
{
if("\(mutableDictionary.objectForKey("\(key)")!)" == "<null>")
{
mutableDictionary.setValue("", forKey: key as! String)
}
else if(mutableDictionary.objectForKey("\(key)")!.isKindOfClass(NSNull))
{
mutableDictionary.setValue("", forKey: key as! String)
}
else if(mutableDictionary.objectForKey("\(key)")!.isKindOfClass(NSDictionary))
{
mutableDictionary.setValue(mutableDictionary.objectForKey("\(key)")!.RemoveNullValueFromDic(), forKey: key as! String)
}
}
return mutableDictionary
}
}
Swift 4 example using reduce
let dictionary = [
"Value": "Value",
"Nil": nil
]
dictionary.reduce([String: String]()) { (dict, item) in
guard let value = item.value else {
return dict
}
var dict = dict
dict[item.key] = value
return dict
}

Swift parse json to struct

I have a struct like
struct Channel {
var id : Int = 0
var name = ""
}
and I am getting json from URL as
{"channel_list":[{"channel_id":0,"channel_name":"test1"},{"channel_id":0,"channel_name":"test2"}]}
However I am not able to get data as
func parseJson(anyObj:AnyObject) -> Array<Channel>{
var list:Array<Channel> = []
if anyObj is Array<AnyObject> {
var b:Channel = Channel()
for json in anyObj as! Array<AnyObject>{
b.id = (json["channel_id"] as AnyObject? as? Int) ?? 0
b.name = (json["channel_name"] as AnyObject? as? String) ?? ""
list.append(b)
}
}
return list
}
//read code
let anyObj: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0),error: nil) as AnyObject?
println(anyObj)
if let myobj=anyObj["channel_list"] as AnyObject {
self.Channellist=self.parseJson(anyObj!)
}
Whats wrong with this?
First, instead of using AnyObject, you should cast the JSON response as a Dictionary: [NSObject:AnyObject] then safe cast the result of anyObj["channel_list"] to an Array of Dictionaries [[NSObject:AnyObject]], because this is your JSON response format.
Then you need to use this type in your parseJSON function. We're also simplifying it while we're at it, because there's no need to do weird castings anymore.
Also, you were passing the wrong argument to your function (you used anyObj instead of myObj).
struct Channel {
var id : Int = 0
var name = ""
}
func parseJson(anyObj: [[NSObject:AnyObject]]) -> Array<Channel>{
var list: Array<Channel> = []
var b: Channel = Channel()
for json in anyObj {
b.id = (json["channel_id"] as? Int) ?? 0
b.name = (json["channel_name"] as? String) ?? ""
list.append(b)
}
return list
}
if let anyObj = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions(0),error: nil) as? [NSObject:AnyObject] {
if let myobj = anyObj["channel_list"] as? [[NSObject:AnyObject]] {
self.Channellist=self.parseJson(myobj)
}
}
There's still room for improvement: you could create an initializer for your Struct, for example, and also create a typealias for the response types, use map to create the list, etc.
Here's how I would do it with Swift 2:
struct Channel {
var id : Int
var name: String
init?(JSON: [NSObject: AnyObject]?) {
guard let channelID = json["channel_id"] as? Int, let channelName = json["channel_name"] as? String
else { name = ""; id = 0; return nil }
name = channelName
id = channelID
}
}
func parseJSON(array: [[NSObject:AnyObject]]) -> [Channel?] {
return array.map { Channel(JSON: $0) }
// If you don't want to return optionals to channel you can do this instead:
// return array.map { Channel(JSON: $0) }.filter { $0 != nil }.map { $0! }
}
// And in the caller
do {
guard let dict = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? [NSObject : AnyObject]
else { throw NSError(domain ... // Setup error saying JSON wasn't parsed. }
guard let arrayContents = dict["channel_list"] as? [[NSObject:AnyObject]]
else { throw NSError(domain ... // Setup error saying array wasn't found. }
let channels = parseJSON(arrayContents)
}
catch {
print(error)
}

Resources