Parsing of JSON by Codable to array - json

I have following JSON:
[
{
"id": 1,
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
37.5741167,
55.7636592
]
},
"properties": {
"hintContent": "переулок Волков, 13с1",
"balloonContentHeader": "переулок Волков, 13с1"
}
]
I am trying to use JSONDecoder:
struct Point : Codable {
let id: Int
let type: String
let properties: Properties
}
struct Properties : Codable {
let hintContent: String
let balloonContentHeader: String
}
struct Points : Codable {
var data : [Point]
}
func parse(fileName: String) {
let url = Bundle.main.url(forResource: fileName, withExtension: "json")
let data = try? Data(contentsOf: url!)
if let jsonPoints = try? JSONDecoder().decode([Point].self, from: data!) {
print(jsonPoints)
}
}
What is wrong?

Make sure your JSON is valid -- this one has a missing }.
Paste your (valid) JSON into app.quicktype.io to generate models ie. make sure that your models match the JSON. In the event that your JSON isn't valid, that site will warn you about it as well.
Always use do/try/catch and not try? so that you can get meaningful errors when JSON decoding fails.
let data = """
[
{
"id": 1,
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
37.5741167,
55.7636592
]
},
"properties": {
"hintContent": "t",
"balloonContentHeader": "t"
}
}
]
""".data(using: .utf8)
struct Point: Codable {
let id: Int
let type: String
let geometry: Geometry
let properties: Properties
}
// MARK: - Geometry
struct Geometry: Codable {
let type: String
let coordinates: [Double]
}
// MARK: - Properties
struct Properties: Codable {
let hintContent, balloonContentHeader: String
}
func parse(fileName: String) {
do {
let jsonPoints = try JSONDecoder().decode([Point].self, from: data!)
print(jsonPoints)
} catch {
print(error)
}
}
Without the adjusted JSON, your original code would generate this error in the catch block:
The given data was not valid JSON....Badly formed object around character 224.

Related

How to filter JSON and get value in iOS Swift?

I'm trying to filter JSON and get key & value to parse it. Here all JSON values are dynamic. Right now I need to find "type = object" if the type found is true then I need to check value ={"contentType" & "URL"}.
here is my JSON:
{
"date": {
"type": "String",
"value": "03/04/1982",
"valueInfo": {}
},
"Scanner": {
"type": "Object",
"value": {
"contentType": "image/jpeg ",
"url": "https://www.pexels.com/photo/neon-advertisement-on-library-glass-wall-9832438/",
"fileName": "sample.jpeg"
},
"valueInfo": {
"objectTypeName": "com.google.gson.JsonObject",
"serializationDataFormat": "application/json"
}
},
"startedBy": {
"type": "String",
"value": "super",
"valueInfo": {}
},
"name": {
"type": "String",
"value": "kucoin",
"valueInfo": {}
},
"ScannerDetails": {
"type": "Json",
"value": {
"accountNumber": "ANRPM2537J",
"dob": "03/04/1982",
"fathersName": "VASUDEV MAHTO",
"name": "PRAMOD KUMAR MAHTO"
},
"valueInfo": {}
}
}
decode code:
AF.request(v , method: .get, parameters: nil, encoding: URLEncoding.default, headers: headers).responseJSON { (response:AFDataResponse<Any>) in
print("process instance id api document view list::::",response.result)
switch response.result {
case .success:
let matchingUsers = response.value.flatMap { $0 }.flatMap { $0. == "object" }
print("new object doc:::", matchingUsers)
guard let data = response.value else {
return
}
print("new object doc:::", matchingUsers)
if let newJSON = response.value {
let json = newJSON as? [String: [String:Any]]
print("new object doc:::", json as Any)
// let dictAsString = self.asString(jsonDictionary: json)
let vc = self.stringify(json: json ?? [])
print("dictAsString ::: dictAsString::::==",vc)
let data = vc.data(using: .utf8)!
do{
let output = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: [String:String]]
print ("demo:::==\(String(describing: output))")
}
catch {
print (error)
}
do {
if let jsonArray = try JSONSerialization.jsonObject(with: data, options : .allowFragments) as? [String: [String:String]]
{
print("json array::::",jsonArray) // use the json here
} else {
print("bad json")
}
} catch let error as NSError {
print(error)
}
}
self.view.removeLoading()
case .failure(let error):
print("Error:", error)
self.view.removeLoading()
}
}
How to get specific values from JSON? Any help is much appreciated pls...
Here is code from my playground with your json sample:
import Foundation
let json = """
{
"date": {
"type": "String",
"value": "03/04/1982",
"valueInfo": {}
},
"Scanner": {
"type": "Object",
"value": {
"contentType": "image/jpeg ",
"url": "https://www.pexels.com/photo/neon-advertisement-on-library-glass-wall-9832438/",
"fileName": "sample.jpeg"
},
"valueInfo": {
"objectTypeName": "com.google.gson.JsonObject",
"serializationDataFormat": "application/json"
}
},
"startedBy": {
"type": "String",
"value": "super",
"valueInfo": {}
},
"name": {
"type": "String",
"value": "kucoin",
"valueInfo": {}
},
"ScannerDetails": {
"type": "Json",
"value": {
"accountNumber": "ANRPM2537J",
"dob": "03/04/1982",
"fathersName": "VASUDEV MAHTO",
"name": "PRAMOD KUMAR MAHTO"
},
"valueInfo": {}
}
}
"""
let data = json.data(using: .utf8, allowLossyConversion: false)!
struct ObjectScanner: Decodable {
let contentType: String
let url: String
let fileName: String
}
enum ObjectScannerType {
case object(ObjectScanner)
}
struct Scanner: Decodable {
enum ScannerType: String, Decodable {
case object = "Object"
}
enum CodingKeys: String, CodingKey {
case type, value
}
let scanner: ObjectScannerType
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decode(ScannerType.self, forKey: .type)
switch type {
case .object:
let value = try container.decode(ObjectScanner.self, forKey: .value)
scanner = .object(value)
}
}
}
struct DateResponse: Decodable {
let type: String
let value: String
// let valueInfo // Not enough information in sample for me to decode this object
}
struct Response: Decodable {
enum CodingKeys: String, CodingKey {
case date
case scanner = "Scanner"
}
let date: DateResponse
let scanner: Scanner
}
let decoder = JSONDecoder()
do {
let response = try decoder.decode(Response.self, from: data)
print(response)
} catch {
print("Error decoding: \(error.localizedDescription)")
}
Note: this example is very unforgiving. Any missing value or type that is not supported will lead to a DecodingError. It's up to you to determine all possible types and what is optional and what is not.
I also didn't decode everything nor do I handle the date object to it's fullest
This is as json goes a very complex example. Everything in it is polymorphic: the date, the Scanner, the ScannerDetails, etc. You need to be very careful how you decode this and make sure you handle all possibilities. I would suggest that if you're starting out, you should explore simpler examples.
I also chose to use enums. Not something everyone would chose but its my preference for decoding polymorphic types such as these.
You can read my article about dealing with polymorphic types as well as unknown types here: https://medium.com/#jacob.sikorski/awesome-uses-of-swift-enums-2ff011a3b5a5

How to serialize JSON string to multidimensional NSDictionary

"[{\"person\":\"person1\",\"data\":{\"age\":\"10\",\"name\":\"John\"}},
{\"person\":\"person2\",\"data\":{\"age\":\"20\",\"name\":\"Jonathan\"}},
{\"person\":\"person3\",\"data\":{\"age\":\"30\",\"name\":\"Joe\"}}]"
Note that the value "data" is also a dictionary.
I have a JSON string like above and am trying to serialize like:
if let dataFromString = conf.data(using: .utf8, allowLossyConversion: false) {
let json = try JSON(data: dataFromString)
configuration = json.dictionary ?? [:]
}
However configuration is always an empty dictionary.
You need to parse the JSON you've as an array of dictionaries of type [[String: Any]]. The better modern approach is to use Decodable model to decode the JSON.
let string = """
[
{
"person": "person1",
"data": {
"age": "10",
"name": "John"
}
},
{
"person": "person2",
"data": {
"age": "20",
"name": "Jonathan"
}
},
{
"person": "person3",
"data": {
"age": "30",
"name": "Joe"
}
}
]
"""
let data = Data(string.utf8)
struct Person: Decodable {
let person: String
let data: PersonData
}
struct PersonData: Decodable {
let age, name: String
}
do {
let people = try JSONDecoder().decode([Person].self, from: data)
print(people)
} catch { print(error) }
For the JSON String,
let conf = "[{\"person\":\"person1\",\"data\":{\"age\":\"10\",\"name\":\"John\"}},{\"person\":\"person2\",\"data\":{\"age\":\"20\",\"name\":\"Jonathan\"}},{\"person\":\"person3\",\"data\":{\"age\":\"30\",\"name\":\"Joe\"}}]"
use JSONSerialization's jsonObject(with:options:) method to get the expected response.
if let conf = str.data(using: .utf8 ) {
do {
let dict = try JSONSerialization.jsonObject(with: data, options: []) as? [[String:Any]]
print(dict)
} catch {
print(error)
}
}

Decoding a nested JSON Swift

I have to decode this type of JSON that is downloaded by a response.
The JSON is this, I need retrieve the "gallery" of all items
JSON: https://pastebin.com/KnEwZzxd
I have tried many solution but I am not able to create a Decode of this son.
I have posted the full code on pastebin, too.
{
"status": 200,
"data": {
"date": "2018-07-29T00:00:00.300Z",
"featured": [
{
"id": "5b56298d781e197186378f50",
"name": "Sun Tan Specialist",
"price": "1,500",
"priceIcon": "vbucks",
"priceIconLink": "https://image.fnbr.co/price/icon_vbucks.png",
"images": {
"icon": "https://image.fnbr.co/outfit/5b56298d781e197186378f50/icon.png",
"png": "https://image.fnbr.co/outfit/5b56298d781e197186378f50/png.png",
"gallery": "https://image.fnbr.co/outfit/5b56298d781e197186378f50/gallery.jpg",
"featured": "https://image.fnbr.co/outfit/5b56298d781e197186378f50/featured.png"
},
"rarity": "epic",
"type": "outfit",
"readableType": "Outfit"
},
{
"id": "5b562af2781e19db65378f5c",
"name": "Rescue Paddle",
"price": "800",
"priceIcon": "vbucks",
"priceIconLink": "https://image.fnbr.co/price/icon_vbucks.png",
"images": {
"icon": "https://image.fnbr.co/pickaxe/5b562af2781e19db65378f5c/icon.png",
"png": "https://image.fnbr.co/pickaxe/5b562af2781e19db65378f5c/png.png",
"gallery": "https://image.fnbr.co/pickaxe/5b562af2781e19db65378f5c/gallery.jpg",
"featured": false
},
"rarity": "rare",
"type": "pickaxe",
"readableType": "Pickaxe"
}
],
"daily": [
{
"id": "5ab1723e5f957f27504aa502",
"name": "Rusty Rider",
"price": "1,200",
"priceIcon": "vbucks",
"priceIconLink": "https://image.fnbr.co/price/icon_vbucks.png",
"images": {
"icon": "https://image.fnbr.co/glider/5ab1723e5f957f27504aa502/icon.png",
"png": "https://image.fnbr.co/glider/5ab1723e5f957f27504aa502/png.png",
"gallery": "https://image.fnbr.co/glider/5ab1723e5f957f27504aa502/gallery.jpg",
"featured": "https://image.fnbr.co/glider/5ab1723e5f957f27504aa502/featured.png"
},
"rarity": "epic",
"type": "glider",
"readableType": "Glider"
},
{
"id": "5b0e944bdb94f1a4bbc0a8e4",
"name": "Rambunctious",
"price": "500",
"priceIcon": "vbucks",
"priceIconLink": "https://image.fnbr.co/price/icon_vbucks.png",
"images": {
"icon": "https://image.fnbr.co/emote/5b0e944bdb94f1a4bbc0a8e4/icon.png",
"png": "https://image.fnbr.co/emote/5b0e944bdb94f1a4bbc0a8e4/png.png",
"gallery": "https://image.fnbr.co/emote/5b0e944bdb94f1a4bbc0a8e4/gallery.jpg",
"featured": false
}
]
}
}
Aside from posting the JSON Code itself, it would be useful to actually show an attempt as to how you have attempted to decode it as well ^_________^.
Anyway, the best way to tackle this issue is to use custom Structs and the Decodable Protocol to handle the JSON response.
From your JSON you will initially get a two values:
/// The Initial Response From The Server
struct Response: Decodable {
let status: Int
let data: ResponseData
}
From this we then map the 'data' to a struct called ResponseData:
/// The Data Object
struct ResponseData: Decodable{
let date: String
let featured: [Product]
let daily: [Product]
}
In this we have two variables which contain an array of identical struct which I have called Product:
/// The Product Structure
struct Product: Decodable{
let id: String
let name: String
let price: String
let priceIcon: String
let priceIconLink: String
let images: ProductImages
let rarity: String
let type: String
let readableType: String
}
Within this we have one variable which is a dictionary (images) which we then map to another struct:
/// The Data From The Product Images Dictionary
struct ProductImages: Decodable{
let icon: String
let png: String
let gallery: String
///The Featured Variable For The Product Images Can Contain Either A String Or A Boolean Value
let featured: FeaturedType
}
The issue you have with the ProductImages, is that the featured var sometimes contains a String but on others it contains a Bool value. As such we need to create a custom struct to handle the decoding of this to ensure we always get a String (I am probably not doing this right way so if someone has a better solution please say so):
/// Featured Type Returns A String Of Either The Boolean Value Or The Link To The JPG
struct FeaturedType : Codable {
let formatted: String
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
do {
//1. If We Get A Standard Response We Have A String
let stringResult = try container.decode(String.self)
formatted = stringResult
} catch {
//2. On Occassions We Get An Bool
let boolResult = try container.decode(Bool.self)
formatted = String(boolResult)
}
}
}
Now that is the basic structure of your JSON so now you need to handle it. In this example I am loading the JSON from the MainBundle as I dont have the actual URL.
/// Loads & Decodes The JSON File
func retreiveJSON(){
//1. Load The JSON File From The Main Bundle
guard let jsonURL = Bundle.main.url(forResource: "sample", withExtension: ".json") else { return }
do{
//2. Get The Data From The URL
let data = try Data(contentsOf: jsonURL)
//3. Decode The JSON
let jsonData = try JSONDecoder().decode(Response.self, from: data)
//4. Extract The Data
extractDataFrom(jsonData)
}catch{
print("Error Processing JSON == \(error)")
}
}
In the above function you will notice I am calling the function extractDataFrom() which allows you to then do what you need to do with your data:
/// Extracts The Daily & Featured Products From The JSON
///
/// - Parameter jsonData: Response
func extractDataFrom(_ jsonData: Response){
//1. Get The Daily Products
let dailyProducts = jsonData.data.daily
dailyProducts.forEach { (product) in
print(product.id)
print(product.name)
print(product.price)
print(product.priceIcon)
print(product.priceIconLink)
print(product.images)
print(product.rarity)
print(product.type)
print(product.readableType)
}
//2. Get The Featured Products
let featuredProducts = jsonData.data.featured
featuredProducts.forEach { (product) in
print(product.id)
print(product.name)
print(product.price)
print(product.priceIcon)
print(product.priceIconLink)
print(product.images)
print(product.rarity)
print(product.type)
print(product.readableType)
}
}
If you wanted to save this data then all you would need to do is add the following variables under your class declaration e.g:
var featuredProducts = [Product]()
var dailyProducts = [Product]()
And in the extractDataFrom() function change the:
let dailyProducts
let featuredProducts
To:
dailyProducts = jsonData.data.daily
featuredProducts = jsonData.data.featured
Please note that this is a very crude example, and as noted, I may not be handling the 'featured' variable correctly.
Hope it helps...
Thanks to quicktype and other services converting valid json to Swift and other languages is simple. Editing it to fit your needs should be simple enough.
// To parse the JSON, add this file to your project and do:
//
// let welcome = try? JSONDecoder().decode(Welcome.self, from: jsonData)
import Foundation
struct Welcome: Codable {
let status: Int
let data: DataClass
}
struct DataClass: Codable {
let date: String
let featured, daily: [Daily]
}
struct Daily: Codable {
let id, name, price, priceIcon: String
let priceIconLink: String
let images: Images
let rarity, type, readableType: String?
}
struct Images: Codable {
let icon, png, gallery: String
let featured: Featured
}
enum Featured: Codable {
case bool(Bool)
case string(String)
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let x = try? container.decode(Bool.self) {
self = .bool(x)
return
}
if let x = try? container.decode(String.self) {
self = .string(x)
return
}
throw DecodingError.typeMismatch(Featured.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Featured"))
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .bool(let x):
try container.encode(x)
case .string(let x):
try container.encode(x)
}
}
}

Decode Custom Json with Decodable

I have this Json:
{ "first": {
"house": [
"small"
]
}, "second": {
"house": [
"small"
] }, "third": {
"car": [
"fast",
"economic"
] }, "fourth": {
"car": [
"fast",
"economic"
] }, "fifth": {
"car": [
"fast",
"economic"
],
"ice": [
"round",
"tasty"
],
"tree": [
"big",
"small"
] } }
I tried to set up a structure with Decodable but I do not get it to work:
struct secondLayer: Codable {
let exchange: [String: [String]]
}
struct decodeJson: Codable {
let symbol: [String: [secondLayer]]
static func decode(jsonString: String) - [decodeJson] {
var output = [decodeJson]()
let decode = JSONDecoder()
do {
let json = jsonString.data(using: .utf8)
output = try! decode.decode([decodeJson].self, from: json!)
} catch {
print(error.localizedDescription)
}
return output
}
}
I get this Error:
Fatal error: 'try!' expression unexpectedly raised an error:
Swift.DecodingError.typeMismatch(Swift.Array<Any>,
Swift.DecodingError.Context(codingPath: [], debugDescription:
"Expected to decode Array<Any but found a dictionary instead.",
underlyingError: nil)): file
/BuildRoot/Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-900.0.74.1/src/swift/stdlib/public/core/ErrorType.swift,
line 181
I tried some modification but I do not get it to work.
The error message
"Expected to decode Array<Any> but found a dictionary instead."
is very clear. You want to decode an array ([decodeJson]) but the root object is a dictionary (starting with {)
Your code cannot work anyway. There are no keys exchange and symbol in the JSON.
Basically there are two ways to decode that JSON:
If all keys are dynamic you cannot decode the JSON to structs. You have to decode it to [String:[String:[String]]]. In this case Codable has no benefit over traditional JSONSerialization.
struct DecodeJson: Codable {
static func decode(jsonString: String) -> [String:[String:[String]]] {
var output = [String:[String:[String]]]()
let decoder = JSONDecoder()
do {
let json = Data(jsonString.utf8)
output = try decoder.decode([String:[String:[String]]].self, from: json)
print(output)
} catch {
print(error.localizedDescription)
}
return output
}
}
Or if the ordinal keys first, second etc are static use an umbrella struct
struct Root : Codable {
let first : [String:[String]]
let second : [String:[String]]
let third : [String:[String]]
let fourth : [String:[String]]
let fifth : [String:[String]]
}
struct DecodeJson {
static func decode(jsonString: String) -> Root? {
let decoder = JSONDecoder()
do {
let json = Data(jsonString.utf8)
let output = try decoder.decode(Root.self, from: json)
return output
} catch {
print(error.localizedDescription)
return nil
}
}
}
Of course you can decode house, car etc into a struct but this requires a custom initializer for each struct because you have to decode a single array manually with unkeyedContainer

Order of JSON node changes while making POST request Swift

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"}}}}}