I am new to swift . I created simple playground and added the file with extension json into playground . I am trying to decode the result and print the ID into console but any reason ,it not printing the result into console , I do not see error into console window ..
Here is the playground project structure ..
Here is my json file ..
let json = """
{
"id": "1",
"options": [
{
"id": "11",
"options": [
{
"id": "111",
"options": []
}
]
},
{
"id": "2",
"options": [
{
"id": "21",
"options": []
},
{
"id": "22",
"options": [
{
"id": "221",
"options": []
}
]
}
]
}
]
}
Here is the code .. I tried ..
struct People: Codable {
let id: String
let options: [People]
}
func loadJson(filename fileName: String) -> People? {
if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
let jsonData = try decoder.decode(People.self, from: data)
print(jsonData.id)
return jsonData
} catch {
print("error:\(error)")
}
}
return nil
}
It not printing the ID of the decode json ..
So I did get it to print the ID, I changed the people file name to people.json and changed the contents to:
{
"id": "1",
"options": [{
"id": "11",
"options": [{
"id": "111",
"options": []
}]
},
{
"id": "2",
"options": [{
"id": "21",
"options": []
},
{
"id": "22",
"options": [{
"id": "221",
"options": []
}]
}
]
}
]
}
(Notice I removed the let json = statement.)
After that in the playground where define the People struct and the loadJson method you can call it like so:
loadJson(filename: "people")
So now you end up with:
struct People: Codable {
let id: String
let options: [People]
}
func loadJson(filename fileName: String) -> People? {
if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
let jsonData = try decoder.decode(People.self, from: data)
print(jsonData.id)
return jsonData
} catch {
print("error:\(error)")
}
}
return nil
}
loadJson(filename: "people")
Related
I am new to swift . I created simple playground and added the file with extension json into playground . I am trying to decode the result and print the ID by using for loop but , I am getting following error ..
For-in loop requires 'People.Type' to conform to 'Sequence'
Here is my json file ..
{
"id": "1",
"options": [{
"id": "11",
"options": [{
"id": "111",
"options": []
}]
},
{
"id": "2",
"options": [{
"id": "21",
"options": []
},
{
"id": "22",
"options": [{
"id": "221",
"options": []
}]
}
]
}
]
}
Here is the code .. I tried ..
struct People: Codable {
let id: String
let options: [People]
}
func loadJson(filename fileName: String) -> People? {
if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
let jsonData = try decoder.decode(People.self, from: data)
print("// Printing the ID of the Decode Json")
for jsondata in jsonData {
print("ID: \(jsonData.id)")
}
return jsonData
} catch {
print("error:\(error)")
}
}
return nil
}
loadJson(filename: "people1")
Here is the screenshot of the error ..
Here I got to run exactly what you are asking for, however I think you should probably consider renaming your struct to something like Person as #Vadian suggested.
struct People: Codable {
let id: String
let options: [People]
}
func loadJson(filename fileName: String) -> People? {
if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
let jsonData = try decoder.decode(People.self, from: data)
printPeople(people: [jsonData])
return jsonData
} catch {
print("error:\(error)")
}
}
return nil
}
func printPeople(people: [People]) {
for person in people {
print(person.id)
if (!person.options.isEmpty) {
printPeople(people: person.options)
}
}
}
loadJson(filename: "people")
This will print:
1
11
111
2
21
22
221
I have called an API to get all holidays in a year, it came out a Json type. But I only can extract it like below (it is one of many elements of "items")
"items": [
{
"kind": "calendar#event",
"etag": "\"3235567993214000\"",
"id": "20200101_1814eggq09ims8ge9pine82pclgn49rj41262u9a00oe83po05002i01",
"status": "confirmed",
"htmlLink": "https://www.google.com/calendar/event?eid=MjAyMDAxMDFfMTgxNGVnZ3EwOWltczhnZTlwaW5lODJwY2xnbjQ5cmo0MTI2MnU5YTAwb2U4M3BvMDUwMDJpMDEgZW4udWsjaG9saWRheUB2",
"created": "2021-04-07T08:26:36.000Z",
"updated": "2021-04-07T08:26:36.607Z",
"summary": "New Year's Day",
"creator": {
"email": "en.uk#holiday#group.v.calendar.google.com",
"displayName": "Holidays in United Kingdom",
"self": true
},
"organizer": {
"email": "en.uk#holiday#group.v.calendar.google.com",
"displayName": "Holidays in United Kingdom",
"self": true
},
"start": {
"date": "2020-01-01"
},
"end": {
"date": "2020-01-02"
},
"transparency": "transparent",
"visibility": "public",
"iCalUID": "20200101_1814eggq09ims8ge9pine82pclgn49rj41262u9a00oe83po05002i01#google.com",
"sequence": 0,
"eventType": "default"
},
{
"kind": "calendar#event",
"etag": "\"3235567993214000\"",
"id": "20200412_1814eggq09ims8gd8lgn6t35e8g56tbechgniag063i0ue048064g0g",
"status": "confirmed",
"htmlLink": "https://www.google.com/calendar/event?eid=MjAyMDA0MTJfMTgxNGVnZ3EwOWltczhnZDhsZ242dDM1ZThnNTZ0YmVjaGduaWFnMDYzaTB1ZTA0ODA2NGcwZyBlbi51ayNob2xpZGF5QHY",
"created": "2021-04-07T08:26:36.000Z",
"updated": "2021-04-07T08:26:36.607Z",
"summary": "Easter Sunday",
"creator": {
"email": "en.uk#holiday#group.v.calendar.google.com",
"displayName": "Holidays in United Kingdom",
"self": true
},
"organizer": {
"email": "en.uk#holiday#group.v.calendar.google.com",
"displayName": "Holidays in United Kingdom",
"self": true
},
"start": {
"date": "2020-04-12"
},
"end": {
"date": "2020-04-13"
},
"transparency": "transparent",
"visibility": "public",
"iCalUID": "20200412_1814eggq09ims8gd8lgn6t35e8g56tbechgniag063i0ue048064g0g#google.com",
"sequence": 0,
"eventType": "default"
}
I try to get the value in key "start" and key "summary" but I can't.
Xcode told me that "items" is a __NSArrayI type.
What I've tried so far is create a class simple like this (just use to try first, so I didn't make all variable)
class API_Info {
var kind: String?
var etag: String?
var id: String?
var status: String?
var htmlLink: String?
var created: String?
var updated: String?
var summary: String?
init(items: [String:Any]){
self.kind = items["kind"] as? String
self.etag = items["etag"] as? String
self.id = items["id"] as? String
self.status = items["status"] as? String
self.htmlLink = items["htmlLink"] as? String
self.created = items["created"] as? String
self.updated = items["updated"] as? String
self.summary = items["summary"] as? String
}
}
And I parse like this:
guard let items = json!["items"]! as? [API_Info] else{
print("null")
return
}
In this way, else statement was run.
What am I doing wrong, and how can I get the data I want?
Thanks in advance.
Codable is the solution here. Below is the struct I used rather than your class
struct ApiInfo: Codable {
let kind: String
let etag: String
let id: String
let status: String
let htmlLink: String
let created: Date
let updated: Date
let summary: String
}
Then I created a root type to hold the array
struct Result: Codable {
let items: [ApiInfo]
}
And then the decoding
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(formatter)
do {
let result = try decoder.decode(Result.self, from: data)
print(result.items)
} catch {
print(error)
}
Notice that the data values are decoded to Date objects.
Optionally you can skip the root type and decode as a dictionary
do {
let items = try decoder.decode([String: [ApiInfo]].self, from: data)
if let values = items["items"] {
print(values)
}
} catch {
print(error)
}
How to parse local JSON data where nested (optional) property is same as main.
Items data may be available or may not be available.
struct Category: Identifiable, Codable {
let id: Int
let name: String
let image: String
var items: [Category]?
}
I am using common Bundle extension to parse JSON data.
extension Bundle {
func decode<T: Codable>(_ file: String) -> T {
guard let url = self.url(forResource: file, withExtension: nil) else {
fatalError("Failed to locate \(file) in bundle.")
}
guard let data = try? Data(contentsOf: url) else {
fatalError("Failed to load \(file) from bundle.")
}
let decoder = JSONDecoder()
let formatter = DateFormatter()
formatter.dateFormat = "y-MM-dd"
decoder.dateDecodingStrategy = .formatted(formatter)
guard let loaded = try? decoder.decode(T.self, from: data) else {
fatalError("Failed to decode \(file) from bundle.")
}
return loaded
}
}
For eg data :
[
{
"id": 1,
"name": "Apple",
"image": "img_url",
"items" : [
{
"id": 1,
"name": "iPhone",
"image": "img_url",
"items" : [
{
"id": 1,
"name": "iPhone 11 Pro",
"image": "img_url"
},
{
"id": 2,
"name": "iPhone 11 Pro Max",
"image": "img_url"
}
]
},
{
"id": 2,
"name": "iPad",
"image": "img_url",
"items" : [
{
"id": 1,
"name": "iPad mini",
"image": "img_url"
},
{
"id": 2,
"name": "iPad Air",
"image": "img_url"
},
{
"id": 3,
"name": "iPad Pro",
"image": "img_url"
}
]
}
]
},
{
"id": 2,
"name": "Samsung",
"image": "img_url",
"items" : [
{
"id": 1,
"name": "Phone",
"image": "img_url"
},
{
"id": 2,
"name": "Tablet",
"image": "img_url"
}
]
}
]
Nesting is not the issue here, You are facing an Array of Contents. so you should pass [Content] to the decoder like:
let jsonDecoder = JSONDecoder()
try! jsonDecoder.decode([Category].self, from: json)
🎁 Property Wrapper
You can implement a simple property wrapper for loading and decoding all of your properties:
#propertyWrapper struct BundleFile<DataType: Decodable> {
let name: String
let type: String = "json"
let fileManager: FileManager = .default
let bundle: Bundle = .main
let decoder = JSONDecoder()
var wrappedValue: DataType {
guard let path = bundle.path(forResource: name, ofType: type) else { fatalError("Resource not found") }
guard let data = fileManager.contents(atPath: path) else { fatalError("File not loaded") }
return try! decoder.decode(DataType.self, from: data)
}
}
Now you can have any property that should be loaded from a file in a Bundle like:
#BundleFile(name: "MyFile")
var contents: [Content]
Note that since the property should be loaded from the bundle, I raised a FatalError. Because the only person should be responsible for these errors is the developer at the code time (not the run time).
I was trying to post the following jSON body
request JSON :
let parameters = [
"createTransactionRequest": [
"merchantAuthentication": [
"name": "xxxxxxxx",
"transactionKey": "xxxxxxxxx"
],
"refId": "123456",
"transactionRequest": [
"transactionType": "authCaptureTransaction",
"amount": "5",
"payment": [
"opaqueData": [
"dataDescriptor": desc!,
"dataValue": tocken!
]
]
]
]
]
When I am trying to print(parameters) the order of node changes it looks like
["createTransactionRequest":
["refId": "123456",
"transactionRequest":
["payment": ["opaqueData": ["dataDescriptor": "COMMON.ACCEPT.INAPP.PAYMENT", "dataValue": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx="]],
"transactionType": "authCaptureTransaction",
"amount": "5"],
"merchantAuthentication": ["name": "xxxxxxx", "transactionKey":
"6gvE46G5seZt563w"]
]
]
I am getting response like
{ messages = {
message = (
{
code = E00003;
text = "The element 'createTransactionRequest' in namespace
'AnetApi/xml/v1/schema/AnetApiSchema.xsd' has invalid child element
'refId' in namespace 'AnetApi/xml/v1/schema/AnetApiSchema.xsd'. List of
possible elements expected: 'merchantAuthentication' in namespace
'AnetApi/xml/v1/schema/AnetApiSchema.xsd'.";
}
);
resultCode = Error;
};
}
This is really annoying. anyones help will be highly grateful.
You need to change your data structure as follow:
struct Transaction: Codable {
let createTransactionRequest: CreateTransactionRequest
}
struct CreateTransactionRequest: Codable {
let merchantAuthentication: MerchantAuthentication
let refId: String
let transactionRequest: TransactionRequest
}
struct MerchantAuthentication: Codable {
let name: String
let transactionKey: String
}
struct TransactionRequest: Codable {
let transactionType: String
let amount: String
let payment: Payment
}
struct Payment: Codable {
let opaqueData: OpaqueData
}
struct OpaqueData: Codable {
let dataDescriptor: String
let dataValue: String
}
Testing
let json = """
{ "createTransactionRequest":
{ "merchantAuthentication":
{ "name": "YOUR_API_LOGIN_ID",
"transactionKey": "YOUR_TRANSACTION_KEY"
},
"refId": "123456",
"transactionRequest":
{ "transactionType": "authCaptureTransaction",
"amount": "5",
"payment":
{ "opaqueData":
{ "dataDescriptor": "COMMON.ACCEPT.INAPP.PAYMENT",
"dataValue": "PAYMENT_NONCE_GOES_HERE"
}
}
}
}
}
"""
let jsonData = Data(json.utf8)
do {
let transaction = try JSONDecoder().decode(Transaction.self, from: jsonData)
print(transaction)
// encoding
let encodedData = try JSONEncoder().encode(transaction)
print(String(data: encodedData, encoding: .utf8)!)
} catch {
print(error)
}
{"createTransactionRequest":{"merchantAuthentication":{"name":"YOUR_API_LOGIN_ID","transactionKey":"YOUR_TRANSACTION_KEY"},"refId":"123456","transactionRequest":{"transactionType":"authCaptureTransaction","amount":"5","payment":{"opaqueData":{"dataValue":"PAYMENT_NONCE_GOES_HERE","dataDescriptor":"COMMON.ACCEPT.INAPP.PAYMENT"}}}}}
I can successfully parse all data from my json file. In my application in the collection view i try to parse only 1 image data to cell but i got related all data with it. I'll share the JSON code and the parse code with you and lastly screen shot of simulator. I hope you can help me about it.
Thank you,
JSON code
{
"retcode": "200",
"content": [{
"id": 3,
"name": "X Treme",
"desc": "Polikarbon G\u00f6vde",
"category": "Design",
"thumbnail": [{
"id": 2,
"thumbnail": "http:\/\/www.ertonga.com\/product_images\/xtreme_red.jpg"
}, {
"id": 3,
"thumbnail": "http:\/\/www.ertonga.com\/product_images\/xtreme_orange.jpg"
}, {
"id": 4,
"thumbnail": "http:\/\/www.ertonga.com\/product_images\/xtreme_blue.jpg"
}, {
"id": 5,
"thumbnail": "http:\/\/www.ertonga.com\/product_images\/xtreme_green.jpg"
}, {
"id": 6,
"thumbnail": "http:\/\/www.ertonga.com\/product_images\/xtreme_clear.jpg"
}, {
"id": 7,
"thumbnail": "http:\/\/www.ertonga.com\/product_images\/xtreme_grey.jpg"
}, {
"id": 8,
"thumbnail": "http:\/\/www.ertonga.com\/product_images\/xtreme_slred.jpg"
}]
}, {
"id": 4,
"name": "Opal",
"desc": "Polikarbon Sandalye",
"category": "Design",
"thumbnail": [{
"id": 9,
"thumbnail": "http:\/\/www.ertonga.com\/product_images\/opal_orange.jpg"
}, {
"id": 10,
"thumbnail": "http:\/\/www.ertonga.com\/product_images\/opal_blue.jpg"
}, {
"id": 11,
"thumbnail": "http:\/\/www.ertonga.com\/product_images\/opal_green.jpg"
}]
}],
"error_msg": ""
}
Swift Code
if let url = NSURL(string: urlString) {
if let data = try? NSData(contentsOfURL: url, options: [])
{
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments)
var valueforname:Int = 0
if let blogs = json["content"] as? [[String: AnyObject]] {
for blog in blogs {
if let blog2 = blog["thumbnail"] as? [[String: AnyObject]] {
for blog3 in blog2 {
if let blog4 = blog3["thumbnail"] as? String {
var checkvalue1 = blog3["id"] as? Int
if Imagearray.contains(String(checkvalue1!)) {
}
else {
Imagearray.append(blog4)
}
}
}
}
}
}
}
catch {
print("error serializing JSON: \(error)")
}
}
}
and here is the screen shots. You can see the different colors of chairs I want to only 1 color for each item
Try this one:
NSURL(string: urlString) {
if let data = try? NSData(contentsOfURL: url, options: [])
{
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments)
var valueforname:Int = 0
if let blogs = json["content"] as? [[String: AnyObject]] {
for blog in blogs {
if let blog2 = blog["thumbnail"] as? [[String: AnyObject]] {
if let blog4 = blog2["thumbnail"] as? String
{
Imagearray.append(blog4)
}
}
}
}
}
catch {
print("error serializing JSON: \(error)")
}
}
}