Reading JSON properties without structure/model - ios

How can we extract arrays from server response JSON strings?
The JSON object is serialized as such:
do {
if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
print(json) // output below
}
} catch let error {
print(error.localizedDescription)
}
and the resulting output is:
["locations": <__NSArrayM 0x280e23f90>(
{
lat = "123.111111";
long = "1.111111";
},
{
lat = "123.222222";
long = "1.222222";
},
{
lat = "123.333333";
long = "1.333333";
}
),
"mobileNo": 55551234,
"clientID": 2123456789,
"date": 10082019,
"time": 1854]
How do we read the properties like mobileNo and clientID from the json variable, and more importantly, how do we place the locations array into an array or dictionary?
Thanks for any help.

Use JSONDecoder and Decodable.
Create response structure.
struct Model: Decodable {
struct Location: Decodable {
var lat: String
var long: String
}
var locations: [Location]
var mobileNo: Int
var clientId: Int
var date: Int
var time: Int
}
User JSONDecoder to decode
do {
if let response: Model = try JSONDecoder().decode(Model.self, for: data) {
print(response)
}
} catch let error {
print(error.localizedDescription)
}

Related

Fetch and Check internal (Local) JSON file in swift

I want to check city name From local JSON if found then massage show "You got it". But one problem came occurred that is I fetch data from file is successfully but I'd know how to compare it.
Here is my JSON file look like:
{
"data": [
{
"zip_code": 55001,
"latitude": 44.90717,
"longitude": -92.816193,
"city": "Afton",
"state": "MN",
"county": "Washington"
},
{
"zip_code": 55002,
"latitude": 45.513447,
"longitude": -92.894239,
"city": "Almelund",
"state": "MN",
"county": "Chisago"
}
]
}
Code is here:
func FatchingInformation(){
do {
if let file = Bundle.main.url(forResource: "Zone", withExtension: "json") {
let data = try Data(contentsOf: file)
let json = try JSONSerialization.jsonObject(with: data, options: [])
if let object = json as? [String: Any] {
// This condition work and Get JSON on Debug Area
print("Obj is ::: \(object)")
} else if let object = json as? [Any] {
// json is an array
print("Object is \(object)")
} else {
print("JSON is invalid")
}
} else {
print("no file")
}
} catch {
print(error.localizedDescription)
}
}
you are in right way on your JSON is dict of array of dictionary, you need to iterate your [String: Any] first there after check it contains array or dict then you need to follow as below
if let object = json as? [String: Any], let objectArray = object["data"] as? [[String: Any]] {
// do stuff
for getDictItems in objectArray{
if let getCityCompare = getDictItems["city"] as? String, !getCityCompare.isEmpty, getCityCompare == "Almelund"{
print("city name is \(getCityCompare)")
break
}
}
}
You ca use decodable Struct to decode json
// MARK: - Address
struct Address: Codable {
let data: [Datum]
}
// MARK: - Datum
struct Datum: Codable {
let zipCode: Int
let latitude, longitude: Double
let city, state, county: String
enum CodingKeys: String, CodingKey {
case zipCode = "zip_code"
case latitude, longitude, city, state, county
}
}
let address = try? JSONDecoder().decode(Address.self, from: jsonData)

How to convert stored values to JSON format using Swift?

I am trying to convert stored coredata values to JSON format and the JSON format value need to assign a single variable, because this generated JSON I need to send to server. Below code I tried to get coredata stored values but don’t know how to generate JSON required format.
Getting values from coredata
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "User")
do {
let results = try context.fetch(fetchRequest)
let dateCreated = results as! [Userscore]
for _datecreated in dateCreated {
print("\(_datecreated.id!)-\(_datecreated.name!)") // Output: 79-b \n 80-c \n 78-a
}
} catch let err as NSError {
print(err.debugDescription)
}
Need to Convert Coredata Value to Below JSON format
{
    "status": true,
    "data": [
        {
            "id": "20",
            "name": "a"
        },
        {
            "id": "21",
            "name": "b"
        },
        {
            "id": "22",
            "name": "c"
        }
    ]
}
Probably the easiest is to convert your object(s) to either dictionaries or arrays (depending on what you need).
First you need to be able to convert your Userscore to dictionary. I will use extension on it since I have no idea what your entity looks like:
extension Userscore {
func toDictionary() -> [String: Any]? {
guard let id = id else { return nil }
guard let name = name else { return nil }
return [
"id": id,
"name": name
]
}
}
Now this method can be used to generate an array of your dictionaries simply using let arrayOfUserscores: [[String: Any]] = userscores.compactMap { $0.toDictionary() }.
Or to build up your whole JSON as posted in question:
func generateUserscoreJSON(userscores: [Userscore]) -> Data? {
var payload: [String: Any] = [String: Any]()
payload["status"] = true
payload["data"] = userscores.compactMap { $0.toDictionary() }
return try? JSONSerialization.data(withJSONObject: payload, options: .prettyPrinted)
}
This will now create raw data ready to be sent to server for instance
var request = URLRequest(url: myURL)
request.httpBody = generateUserscoreJSON(userscores: userscores)
You can use the properties of an Encodable to make this happen. This has the added benefit of not resorting to the Any type.
For the JSON, you could use the following types:
struct JSONMessage: Encodable {
var status: Bool
var data: [JSONDataEntry]
}
struct JSONDataEntry: Encodable {
var id: String
var name: String
}
Then you can adjust your do/try/catch as follows:
do {
let results = try context.fetch(fetchRequest)
let dateCreated = results as! [Userscore]
// *starting here*
let data = dateCreated.map { JSONDataEntry(id: String($0.id!), name: $0.name!) }
let status = true // <- not sure where status comes from, so adding here
let message = JSONMessage(status: status, data: data)
let jsonData = try JSONEncoder().encode(message)
if let json = String(data: jsonData, encoding: .utf8) {
// do something with the JSON string
print(json)
}
// *ending here*
} catch let err as NSError {
print(err.debugDescription)
}

How to parse part of json data into table

How to parse following json which I want to parse only few part.
```
{
"head": {
"StatusValue": 200,
"StatusText": "Success"
},
"body": {
"Data": [
{
"payer_type_id": 1,
"payer_type": "Self Pay"
},
{
"payer_type_id": 2,
"payer_type": "Corporate"
},
{
"payer_type_id": 6,
"payer_type": "Insurance"
}
],
"RecordCount": 3,
"TotalRecords": null
}
}
How to parse only data inside Data key.
Expected result should be in following format
Date = [["payer_type_id": 1,"payer_type": "Self Pay"],["payer_type_id": 2,"payer_type": "Corporate"],["payer_type_id": 6,"payer_type": "Insurance"]]
You can use Codable protocol for your parsing .
Create Your Model
struct APIRootModel : Codable {
let head : HeaderModel
let body : BodyModel
}
struct HeaderModel :Codable{
let StatusValue : Int
let StatusText : String
}
struct BodyModel : Codable{
let Data : [DataModel]
}
struct DataModel : Codable{
let payer_type_id : Int
let payer_type : String
}
Decode your json using JSONDecoder()
let decoder = JSONDecoder()
do {
let rootModel = try decoder.decode(APIRootModel.self, from: jsonData)
print(rootModel.body.data)
} catch {
print(error)
}
Use [DataModel] for your tableview datasource.
Try this:
let jsonString = "your json string here"
let dictObject = getDictionaryFromJsonString(dictString: jsonString)
let bodyDict = dictObject["body"]
let dataArray = bodyDict["Data"]
func getDictionaryFromJsonString(dictString:String)->
[String: Any] {
do {
return try JSONSerialization.jsonObject(with:
dictString.data(using: String.Encoding.utf8,
allowLossyConversion: true)!, options:
JSONSerialization.ReadingOptions.allowFragments) as!
Dictionary
} catch {
return [:]
}
}

How to map [[String:AnyObject]] to a class from a string

I'm trying to add an Array of objects from a string. I am getting an encrypted string from an API call. After decrypting the data, getting as a string of JSON structure as below:
{
"request_id”:”abcds123”,
"status”:”18”,
"message":"SUCCESS",
"customer_name”:”XXXX X XX”,
"mobile_no”:”123456789”,
"email_id":"xxx#xxx.com",
“function”:
[
{“funcCode”:”OPN”,,”funcVal”:0,”funcType":"M"},
{“funcCode”:”CLO”,”funcVal”:1,”funcType":"M"}
],
“dataID”:”ASD1234”,
”valid”:”Y”
}
This is generic API response which will very based on response.
I can map "function" element to [[String:AnyObject]]. But not able to map it to class directly. Do we have an easier approach to extract "function" array to a class in swift without iterating the data and add to an array variable:
var mainObject : [String:Any] = [:]
if let data = result.data(using: .utf8) {
do {
mainObject = (try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any])!
if let status = mainObject[“status”] as? String {
switch(status) {
case 200:
if mainObject.keys.contains(“customer_name”) {
//do something
}
if mainObject.keys.contains(“function”) {
if let functionList = mainObject[“function”] as? [[String:AnyObject]] {
//map functionList to class [Function]
for function in functionList {
print(“Function ====> ", function)
//create a class and add to an array of Function class
}
}
}
}
} catch {
print(error.localizedDescription)
}
}
}
The result string coming from response.
Objective is to extract "function" data alone and map it to class without creating container class/struct.
You can use the Codable protocol that will help you to map your JSON data into objects.
First create your structs:
struct MyFunction: Codable {
let funcCode: String
let funcVal: Int
let funcType: String
}
struct MyContainer: Codable {
let status: string,
let message: string,
// Another properties
let function: [MyFunction]
}
Then you just have to map your JSON string to the object using the decode function:
if let jsonData = result.data(using: .utf8) {
do {
let decoder = JSONDecoder()
let mainObject = try decoder.decode(MyContainer.self, for: jsonData)
} catch {
print(error.localizedDescription)
}
}
For more information you can check out this blog post.
State of the art is the (De)Codable protocol, you can decode JSON dictionaries directly into structs
struct Main: Decodable {
let status, customerName: String
let function : [Function]
}
struct Function: Codable {
let funcCode, funcType: String
let funcVal : Int
}
let data = Data(result.utf8)
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let mainObject = try decoder.decode(Main.self, from: data)
print(mainObject.customerName)
print(mainObject.function)
} catch {
print(error)
}
Note: A JSON dictionary is never [String:AnyObject] in Swift 3+, it's [String:Any]
Edit: If you want only the Function struct keep the JSONSerialization code, omit the Main struct, declare mainObject as
var functions = [Function]()
and map the JSON array
if let functionList = mainObject["function"] as? [[String:Any]] {
functions = functionList.map {Function(funcCode: $0["funcCode"] as! String,
funcVal: $0["funcVal"] as! Int,
funcType: $0["funcType"] as! String) }
}
To write the array to UserDefaults encode it with JSONEncoder to Data.

struct for nested dictionary API in swift

i'm trying to import JSON data from the v2 of coinmarketcap API. I had it working with v1 as it was an array, however the new version is a dictionary and i cant quite get my struct correct.
The API im using is : https://api.coinmarketcap.com/v2/ticker/?convert=AUD
My struct is set up as below:
struct Coin: Decodable {
private enum CodingKeys: String, CodingKey {
case id = "rank", symbol, name, priceAUD = "quotes"
}
var id: String
var symbol : String
var name : String
var priceAUD : quoteStruct
}
struct quoteStruct{
let aud : priceStruct
}
struct priceStruct{
let price : String
}
My code for fetching the data is:
var coins = [Coin]()
func getCoinData() {
let jsonURL = "https://api.coinmarketcap.com/v2/ticker/?convert=AUD"
let url = URL(string: jsonURL)
URLSession.shared.dataTask(with: url!) { [unowned self] (data, response, error) in
guard let data = data else { return }
do {
self.coins = try JSONDecoder().decode([Coin].self, from: data)
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch {
print("Error is : \n\(error)")
}
}.resume()
}
My code for fetching the data i have used the same as previously which worked with v1 of the API, however i don't think i made my struct correctly.
Thanks in advance!
Your response Changed i try to configure it by converting it to array of dictionary you will need to change quotes to be [String:priceStruct]
struct Coin: Decodable {
private enum CodingKeys: String, CodingKey {
case id,rank,symbol, name, priceAUD = "quotes"
}
var id: Int
var rank: Int
var symbol : String
var name : String
var priceAUD : [String: priceStruct]
}
struct priceStruct : Decodable{
let price : Double
}
func getCoinData() {
var coins = [Coin]()
let jsonURL = "https://api.coinmarketcap.com/v2/ticker/?convert=AUD"
let url = URL(string: jsonURL)
URLSession.shared.dataTask(with: url!) { [unowned self] (data, response, error) in
guard let data = data else { return }
do {
if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any], let resultData = json["data"] as? [String:Any] {
let dataObject = try JSONSerialization.data(withJSONObject: resultData.values.map({$0}) , options: .prettyPrinted)
coins = try JSONDecoder().decode([Coin].self, from: dataObject)
print(coins.count)
}
} catch {
print("Error is : \n\(error)")
}
}.resume()
}
You response in data parameter should be an array rather than a dictionary. You will not be able to iterate a dictionary over undefined keys. It would be good to get your response of API updated first.
But, If you wish to continue with the existing API response, first you need to convert your response in an array and use your Decodable structs as:
struct Coin: Decodable {
var id: String
var symbol : String
var name : String
var priceAUD : QuoteStruct
private enum CodingKeys: String, CodingKey {
case id = "rank", symbol, name, priceAUD = "quotes"
}
}
struct QuoteStruct: Decodable {
let aud : PriceStruct
}
struct PriceStruct: Decodable {
let price : String
}
Update your data parsing in API block as:
guard let responseData = data else { return }
do {
let json = try? JSONSerialization.jsonObject(with: responseData, options: [])
if let jsonData = json as? [String: Any], let dataObject = jsonData["data"] as? [Int: Any] {
let coinArray = dataObject.map { $0.1 }
if let jsonData = try? JSONSerialization.data(withJSONObject: coinArray, options: .prettyPrinted) {
coins = try JSONDecoder().decode([Coin].self, from: jsonData)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
} catch {
print("Error is : \n\(error)")
}

Resources