Here I am having value in JSON in which for some of multiple key value pairs it returning string and for some it is returning array here in custom attributes array in first dictionary in that value key value pair the data present is different and in the second dictionary value key value pair is different here then how to implement the model class for inside array for different key values ?
struct MediaGallery {
let id : Int
let mediaType : String
let label : Any
let position : Int
let disabled : Any
let file : String
init(dict : [String:Any]) {
self.id = (dict["id"] as? Int)!
self.mediaType = (dict["media_type"] as? String)!
self.label = dict["label"]!
self.position = (dict["position"] as? Int)!
self.disabled = dict["disabled"]!
self.file = (dict["file"] as? String)!
}
}
struct AttributeList {
let label : String
let value : String
let code : String
init(dict : [String:Any]){
self.label = (dict["label"])! as! String
self.value = (dict["value"])! as! String
self.code = (dict["code"])! as! String
}
}
struct DetailsListAttribute {
let attributeCode : String
let value : Any
init?(dict : [String:Any]) {
self.attributeCode = dict["attribute_code"] as! String
print(self.attributeCode)
if let values = dict["value"] as? String {
self.value = values
}
else {
if let arr = dict["value"] as? [[String:Any]]{
var filterArr = [AttributeList]()
for obj in arr {
filterArr.append(AttributeList(dict: obj))
}
self.value = filterArr
} else {
self.value = [AttributeList]()
}
}
}
}
I would suggest please save some time by using this great GIT Library ObjectMapper . it will help you to model your object and convert your model objects (classes and structs) to JSON and vice versa.
I've tried multiple JSON-mapping frameworks that were mentioned in Tj3n comment. They all have pros and cons. Apple suggests you to follow the recommendation given here. Also you should check Codable protocol (swift 4 is required).
Ok I don't have the whole JSON, and it doesn't seem clear to me.
But here is how you can parse and create your model Class easily in Swift with the Codable protocol.
You can read more about it and/or some examples, tutorials : Ultimate Guide.
Briefly, what is the Codable protocol ?
You don't need third party library anymore in order to parse and set the json data to your model class.
You juste have to create your class like the JSON is represented. And according to the key-name, it will create the class, properties and everything for you.
Here is an example with your JSON, I don't know if I understood your JSON formatting, but you got the trick :
struct Response: Codable {
let ca: [CustomAttribute]?
enum CodingKeys: String, CodingKey {
case ca = "custom_attributes"
}
}
struct CustomAttribute: Codable {
let code: String?
let value: [Value]?
struct Value: Codable {
let label: String?
let value: String?
let code: String?
let avg: String? // I don't know how your value array is composed
let count: Int? // I don't know how your value array is composed
}
enum CodingKeys: String, CodingKey {
case code = "attribute_code"
case avg = "avg_rating_percent"
}
}
For me, it looks like something like that.
I don't see the whole JSON, but imagine you have the whole JSON as the Response Struct, it contains several objects, like the CustomAttribute Array for example.
Then you can define the CustomAttribute structure, and add as many properties as the JSON has.
Anyway, you can call it this way :
When you have the response from your API call, you can go :
if let data = response.data {
let decoder = JSONDecoder()
let response = try! decoder.decode(Response.self, from: data)
print("Only printing the Custom Attribute : \(response.ca!)")
}
I decode the whole json data as an Object Response (like my Struct).
And I pass to my response callback, or
this might be late but I think this will helps others
The model class which are varies for frameworks like SwiftyJSON, simple swift class, Gloss or swift codable (Swift 4). you can easily generate model class online with your customization jsoncafe.com
Related
I have this kind of data. But I haven’t serialised it yet into JSON
{
"status":"ok",
"totalResults":5899,
"articles":[{//some key value pairs},
{//some key value pairs}
]
}
I want to parse array of articles using decodable protocol.
I know how to do this if I have only articles array but in above case how can I first find the data of articles and parse it to my model using JSONDecodable.
Firstly declare struct of those types.
struct Root : Decodable {
let status : String
let totalResults : Int
let articles : [Article]
}
struct Article : Decodable {
{//some key value pairs},
{//some key value pairs}
}
Suppose the json string is jsonStr.
Now convert this json into data.
let data = Data(jsonStr.utf8)
Now try to decode this data.
let decodedStruct = fromJSON(data)
Here is the definition of fromJSON() method
static func fromJSON(jsonData: Data) -> Root? {
let jsonDecoder = JSONDecoder()
do {
let root = try jsonDecoder.decode(Root.self, from: jsonData)
return root
} catch {
return nil
}
}
A dictionary becomes a struct and an array of dictionaries becomes an array of the struct
struct Root : Decodable {
let status : String
let totalResults : Int
let articles : [Article]
}
struct Article : Decodable {
let aKey : AType
let anotherKey : AnotherType
}
How to parse more then one JSON which each ending with null character(through socket TCP/IP).
{"ObjectID":"UHJvY1dpcmVsZXNzTXNn","DeviceCode":"RUNEOjI=","ActiveInputNames":"Q2hlY2sgaW4gRmFpbA==","DeviceInputNo":"999999","Activation":false,"Reset":true,"LocationID":"","LocationGroupText":"","ProtocolText":"","CallBackNo":"OTE5MTgyNTcyMjQ5"}��{"ObjectID":"VFBpbmdPYmplY3Q="}��
As you can see the above response which has 2 JSON's each ending with null character...I can easily parse the single JSON but unable to parse more then one JSON..
It would be great if any one suggest any solutions!!
First of all separate both the JSONs using components(separatedBy:) so we can parse them individually.
let str = """
{"ObjectID":"UHJvY1dpcmVsZXNzTXNn","DeviceCode":"RUNEOjI=","ActiveInputNames":"Q2hlY2sgaW4gRmFpbA==","DeviceInputNo":"999999","Activation":false,"Reset":true,"LocationID":"","LocationGroupText":"","ProtocolText":"","CallBackNo":"OTE5MTgyNTcyMjQ5"}��{"ObjectID":"VFBpbmdPYmplY3Q="}��
"""
let jsonArr = str.components(separatedBy: "��")
jsonArr contains both the JSON Strings. Let's see how we can parse them.
We'll use Codable to parse both the JSONs using the below model.
struct Root: Codable {
let objectID: String
let deviceCode: String?
let activeInputNames: String?
let deviceInputNo: String?
let activation: Bool?
let reset: Bool?
let locationID: String?
let locationGroupText: String?
let protocolText: String?
let callBackNo: String?
enum CodingKeys: String, CodingKey {
case objectID = "ObjectID"
case deviceCode = "DeviceCode"
case activeInputNames = "ActiveInputNames"
case deviceInputNo = "DeviceInputNo"
case activation = "Activation"
case reset = "Reset"
case locationID = "LocationID"
case locationGroupText = "LocationGroupText"
case protocolText = "ProtocolText"
case callBackNo = "CallBackNo"
}
}
Parse the JSONstrings like,
let parsedObjs = jsonArr.map { (str) -> Root? in
if let data = str.data(using: .utf8) {
do {
let obj = try JSONDecoder().decode(Root.self, from: data)
return obj
} catch {
print(error)
return nil
}
}
return nil
}
parsedObjs will contain parsed Root objects for both the JSON strings.
Let me know if there is any confusion left regarding this.
I answered the same question in Android Yesterday. Here is the Swift Version
let s = "{\"ObjectID\":\"UHJvY1dpcmVsZXNzTXNn\",\"DeviceCode\":\"RUNEOjI=\",\"ActiveInputNames\":\"Q2hlY2sgaW4gRmFpbA==\",\"DeviceInputNo\":\"999999\",\"Activation\":false,\"Reset\":true,\"LocationID\":\"\",\"LocationGroupText\":\"\",\"ProtocolText\":\"\",\"CallBackNo\":\"OTE5MTgyNTcyMjQ5\"}��{\"ObjectID\":\"VFBpbmdPYmplY3Q=\"}��".components(separatedBy: "��")
for string in s{
// do your parsing here
print(string)
}
All you need to do is split the string with �� and you are good to go. Parse the JSON the way you used to.
I apologise for the title of this question. I have no idea what else to call it.
So... When calling the following:
let testData: [NSObject : AnyObject] = getTestData()
print(testData)
I get this output:
[data: {"TypeId":7,"DataList":null,"TypeName":"This is a test"}]
How would I be able to access the value 7 for the key "TypeId"?
EDIT:
Please note that it's holding { } brackets, not only [ ], thus a cast to NSDictionary is not possible as far as I have tried.
Kind regards,
Anders
You can achieve plist-like nested structures using Any type for dictionary values which is Swift's somewhat counterpart to Objective-C's id type but can also hold value types.
var response = Dictionary()
response["user"] = ["Login": "Power Ranger", "Password": "Mighty Morfin'"]
response["status"] = 200
Any seems to be better than AnyObject because in the above code response["status"] is of type Swift.Int, while using value type of AnyObject it is __NSCFNumber.
The way most people do it is to parse annoying JSON data as custom objects. That should be done as soon as you get the JSON. Ideally, data as JSON should not be used outside your communication code, example:
First, let's define a class to hold your server data:
class MyServerObject {
let typeId: Int
let typeName: String
let dataList: [AnyObject]?
init(dictionary: Dictionary<String, AnyObject>) {
let dataDictionary = dictionary["data"] as! Dictionary<String, AnyObject>
self.typeId = dataDictionary["TypeId"] as! Int
self.typeName = dataDictionary["TypeName"] as! String
self.dataList = dataDictionary["DataList"] as? [AnyObject]
}
}
Note that init method is already parsing the JSON. This doesn't have to be done in init, you could also create a static parse method that will return a new instance.
Usage:
// demo data
let jsonString = "{\"data\": {\"TypeId\":7,\"DataList\":null,\"TypeName\":\"This is a test\"}}"
let jsonData = jsonString.dataUsingEncoding(NSUTF8StringEncoding)!
let json = try! NSJSONSerialization.JSONObjectWithData(jsonData, options: [])
// parsing
let myServerObject = MyServerObject(dictionary: json as! Dictionary<String, AnyObject>)
// now we can simply read data as properties
print(myServerObject.typeId)
print(myServerObject.typeName)
One of the good thing about this solution is that we can check the JSON format and all the properties are parsed with the correct types.
Parsing can be hierarchical, for example, if your dataList contains complex objects, let's call them DataListItem, your parsing method can parse each item separately and put them into a [DataListItem], e.g.
if let dataListJSON = dataDictionary["DataList"] as? [Dictionary<String, AnyObject>] {
self.dataList = dataListJSON.map({ DataListItem($0) })
}
Also note that when parsing as! will crash the app when the format is invalid. as? will return nil if the types don't match. as? is very useful for types that can be nil because they are parsed as NSNull instances.
taking in account your data ...
print(testData)
/*
[data: {
DataList = null;
TypeId = 7;
TypeName = "This is a test";
}]
*/
// DataList type should be declared somewhere
class DataList {}
// parse data or set default value, if 'key' doesn't exist
if let data = testData["data"] as? [String:AnyObject] {
let dataList = data["DataList"] as? DataList // nil
let typeId = data["TypeId"] as? Int ?? 0 // 7
let typeName = data["TypeName"] as? String ?? "" // This is test
}
I have the following class which populates all the "Breakfast" entries from a JSON file. Note that in my JSON file, "ingredients" is an array and "instructions" could be an array of arrays.
In the "Populate" function below, Swift is reporting 2 errors saying, "Type 'String' has no member "PopulateArray". "Type 'AnyObject' has no member "PopulateArray".
How do I fix this?
Here's the Swift Source.
import Foundation
class Breakfast
{
var id:String = ""
var name:String = ""
var image:String = ""
var servings:String = ""
var ingredients:[String] = []
var instructions:[AnyObject] = []
func Populate(dictionary:NSDictionary) {
id = dictionary["id"] as! String
name = dictionary["name"] as! String
image = dictionary["image"] as! String
servings = dictionary["servings"] as! String
ingredients = String.PopulateArray(dictionary["ingredients"] as! [NSArray])
instructions = AnyObject.PopulateArray(dictionary["instructions"] as! [NSArray])
}
class func PopulateArray(array:NSArray) -> [Breakfast]
{
var result:[Breakfast] = []
for item in array
{
let newItem = Breakfast()
newItem.Populate(item as! NSDictionary)
result.append(newItem)
}
return result
}
}
The JSON Source can be found here: JSON Source
Neither String nor AnyObject seem to declare a method PopulateArray (unless it's in class extension you haven't included)
If your JSON has contains an array of strings for the "ingredients" key, then it will be an NSArray of NSString, which can be trivially converted to [String]:
ingredients = dictionary["ingredients"] as! [String]
Likewise, if your JSON always contains an array of AnyObject ([AnyObject]) you can populate it with:
instructions = dictionary["instructions"] as! [AnyObject]
Typically when working with JSON, you'll want to use NSJSONSerialization to convert top level JSON data into their corresponding Swift types, here's a quick example of getting data, running it through a serializer and then accessing the JSON data by casting it into known Swift data types:
if let someJsonData = someOptionalData {
if let jsonTopLevelDictionary = try? NSJSONSerialization.JSONObjectWithData(someJsonData, readingOptions: NSJSONReadingOptions.MutableContainers) as? [String:AnyObject] {
if let stringsArr = jsonTopLevelDictionary!["keyWithArrayOfStrings"] as? [String] {
for string in stringsArr {
// do stuff
}
}
}
}
There are a few different ways to skin this cat, you can even check out something like SwiftyJSON which helps parse JSON data into their native swift types.
I have a struct named Jarand I would like to save an array of them to NSUserDefaults. Here is the jar struct code:
struct Jar {
let name: String
let amount: Int
init(name: String, amount: Int){
self.name = name
self.amount = amount
}
}
I belive that I will need to convert this to an NSObject to be able to save it. (Because you can't save a struct directly to NSUserDefaults). My questions are:
How do I convert an array of structs to an NSObject? and How to convert an NSObject back at an array of structs.
This solution is inspired by #Duncan C. I wrote it more familiar way as we do in case Custom Class encoding and decoding.
public struct IRDriver {
public var name: String?
public var amount: Int?
public init() {
}
// Decode
public init(dictionary: Dictionary<String, AnyObject>){
name = dictionary["name"] as? String
amount = dictionary["amount"] as? Int
}
// Encode
public func encode() -> Dictionary<String, AnyObject> {
var dictionary : Dictionary = Dictionary<String, AnyObject>()
dictionary["name"] = name
dictionary["amount"] = amount
return dictionary
}
}
For saving to user defaults you have a couple of options: Have the object conform to NSCoding, or implement methods that convert it to/from an NSDictionary, and save that.
Something like this:
func dictionaryFromJar() -> NSDictionary
{
let dictionary: [AnyObject: AnyObject] = ["name": name, "amount": amount]
return dictionary
}
I think the automatic bridging between Swift dictionaries and NSDictionary would work here, but I'm not positive. My swift is getting a little rusty. :(