I have JSON response in which there is "0" as key value for a JSON. I'm using Swift's Codable protocol to parse my JSON. Is there any way that I can take "0" as my variable's name or some other workaround for this? I know about SwiftyJSON for parsing JSON but I prefer using local swift protocol.
This is my JSON response.
If the "0" key is a constant, you can map it to a valid property name using CodingKeys, like so:
struct Response: Codable {
let success: Bool
let data: DataClass
let error, message: String
}
struct ZeroKeyType: Codable {
// properties go here
}
struct DataClass: Codable {
let the0: ZeroKeyType
let searchField: [String]
enum CodingKeys: String, CodingKey {
case the0 = "0"
case searchField
}
}
Related
When I make an API request, I get returned a JSON structure that contains several objects which all have unique key value names. Therefore I cannot use the regular Decodable protocol to break down the JSON into different instances. However, these key value names are found elsewhere in my JSON structure under known constant values that I can access normally. Here is some example code I created to demonstrate my issue:
let jsonString = """
{
"uniqueObjects": {
"uniqueObject:1132435": {
"firstName": "John"
"lastName": "Smith"
}
"uniqueObject2:119672": {
"firstName": "Jane"
"lastName": "Doe"
}
"uniqueObject3:008997": {
"firstName": "Sam"
"lastName": "Greenfield"
}
}
"keys": {
"object1": {
"key": "uniqueObject1:132435"
}
"object2": {
"key": "uniqueObject2:119672"
}
"object3": {
"key": "uniqueObject3:008997"
}
}
"""
let jsonData = Data(jsonString.utf8)
let decodedData = try? JSONDecoder().decode(JSONData.self, from: jsonData)
print(decodedData?.uniqueObjects.firstObject.firstName ?? "No data decoded")
struct JSONData: Decodable {
let uniqueObjects: Object
let keys: KeyObjects
}
struct Object: Decodable {
let firstObject: Names
let secondObject: Names
let thirdObject: Names
private enum DynamicCodingKeys: String, CodingKey {
case firstObject = "???" // this needs to be mapped to the unique value for object 1
case secondObject = "??" // this needs to be mapped to the unique value for object 2
case thirdObject = "????" // etc.
// I don't think this works because Xcode says that the strings must be raw literals
}
}
struct KeyObjects: Decodable {
let object1: Key
let object2: Key
let object3: Key
}
struct Key: Decodable {
let key: String
}
struct Names: Decodable {
let firstName: String
let lastName: String
}
The approach I took here, which is definitely wrong, was to create a coding key for each of the unique objects and map its name to a String that would be somehow be decoded from its relative key value pair in the key object. CodingKeys, at least what I have tried, do not allow you to do this so I need a new method in order to access this code. I also need to know how I can reference the data once I decode it (just printing it out for now). Help and a short explanation would be much appreciated as I am a beginner developer. Thanks!
Unless I have misunderstood it looks to me like you are over complicating things. This is how I would define the types for decoding the json
struct Response: Codable {
let uniqueObjects: [String: User]
let keys: [String: ObjectKey]
}
struct ObjectKey: Codable {
let key: String
}
struct User: Codable {
let firstName: String
let lastName: String
}
I'm trying to decode some JSON data from a URL and display it in a list. My Json data include special type like in example. How can I solve it?
struct Post : Decodable {
var author : String
var wp:featuredmedia : String // i problem in this line because of json type
}
A little part of the JSON:
[
{
"author":"Asil Arslan",
"wp:featuredmedia":"https://developer.apple.com/assets/elements/icons/swiftui/swiftui-96x96_2x.png"
}
]
You can use custom CodingKeys to create a mapping between the model and the JSON:
struct Post: Decodable {
enum CodingKeys: String, CodingKey {
case author, featuredmedia = "wp:featuredmedia"
}
var author: String
var featuredmedia: String
}
I'm building an app that queries wikidata using json. It works so far, but the issue I'm running into is the response I get from wikidata isn't the actual data, but an identifier. Then, to convert that identifier, I need to send it to another url and receive another json response, but the issue I'm running into is the the key value isn't something I know until I get the first json response in. So, lets say my key is Q1169621. When I run it through the api, I get this response:
I'm using codable and JSONDecoder, but I don't know how to tell the decoder the value of the key in entities is Q1169621 to get at the value I want ("Jim Lauderdale")...some of my code is below, I have structs to define the data of the response, but how do I replace the key value in my struct with the one defined from the previous json that is decoded?
struct InfoFromWikiConverted: Codable {
let entities: Locator //this is the value I need to set before parsing the json
}
struct Locator: Codable {
let labels: Labels //how do I link this to the struct above?
}
struct Labels: Codable {
let en: EN
}
struct EN: Codable {
let value: String
}
The simplest approach would be to decode entities as [String: Locator]:
struct InfoFromWikiConverted: Decodable {
let entities: [String: Locator]
}
Of course, if you want your model to just be a single Locator (which means, potentially ignoring multiple keys under entities), then you'd need to manually decode it.
You'd need to create a type to represent any string Coding Key and implement init(from:):
struct InfoFromWikiConverted: Decodable {
let entities: Locator
struct CodingKeys: CodingKey {
var stringValue: String
var intValue: Int? = nil
init(stringValue: String) { self.stringValue = stringValue }
init?(intValue: Int) { return nil }
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
// get the first key (ignore the rest), and throw if there are none
guard let key = container.allKeys.first else {
throw DecodingError.dataCorrupted(
.init(codingPath: container.codingPath,
debugDescription: "Expected a non-empty object"))
}
let entities = try container.decode(Locator.self, forKey: key)
}
}
Note that because you aren't retaining the ID, you can't encode it back to the same form, so I only conformed to Decodable instead of Codable
I am using Xcode 10.1 and Swift 4.2. When i try to convert JSON response into Codable class it gives an error that Expected to decode Array<Any> but found a string/data instead.
My Actual JSON response is Like this from API .
{
"d": "[{\"Data\":{\"mcustomer\":[{\"slno\":1000000040.0,\"fstname\":null}]},\"Status\":true}]"
}
My Model is Like this
class MainData: Codable{
var d: [SubData]
}
class SubData : Codable {
var Data : Customer
var Status : Bool?
}
class Customer : Codable {
var mcustomer : [Detail]
}
class Detail : Codable {
var slno : Double?
var fstname : String?
}
And I am Decode this Model using JSONDecoder()
let decoder = JSONDecoder()
let deco = try decoder.decode(MainData.self, from: data)
but, I am unable to Decode this Json into My Model.
Your API is wrong. You array in json shouldn't have quotation marks around it. Otherwise you're declaring that value for key "d" is string
"[...]"
[...]
Suggestions:
Variables and constants should start with small capital letter. Otherwise for example your Data property would cause confusion with Data type. For renaming it while decoding you can use CodingKeys
If you don't need to encode your model, you can just implement Decodable protocol
You can use struct instead of class for your model
The top-level JSON object is a dictionary with the key "d" and a string value, representing another JSON object (sometimes called "nested JSON"). If the server API cannot be changed then the decoding must be done in two steps:
Decode the top-level dictionary.
Decode the JSON object from the string obtained in step one.
Together with Robert's advice about naming, CodingKeys and using structs it would look like this:
struct MainData: Codable {
let d: String
}
struct SubData : Codable {
let data : Customer
let status : Bool
enum CodingKeys: String, CodingKey {
case data = "Data"
case status = "Status"
}
}
struct Customer : Codable {
let mcustomer : [Detail]
}
struct Detail : Codable {
let slno : Double
let fstname : String?
}
do {
let mainData = try JSONDecoder().decode(MainData.self, from: data)
let subData = try JSONDecoder().decode([SubData].self, from: Data(mainData.d.utf8))
print(subData)
} catch {
print(error)
}
For your solution to work, the JSON reponse has to be following format
let json = """
{
"d": [
{
"Data": {
"mcustomer": [
{
"slno": 1000000040,
"fstname": null
}
]
},
"Status": true
}
]
}
"""
But, as you can see, the JSON response you are getting is quite different than you are expecting. Either you need to ask to change the response or you need to change your model.
This is my first time taking a shot at Codable/Decodable and i would like to decode a JSON. I am attempting to access the "name" and "description" keys within the events array. Below is a snippet of the JSON - im getting this error within my code
"Expected to decode Dictionary but found an array instead."
"pagination": {
"page_number": 1,
"page_size": 50,
"continuation": "eyJwYWdlIjogMn0",
"has_more_items": true
},
"events": [
{
"name": {
"text": "Genesis Part 4",
"html": "Genesis Part 4"
},
"description": {
"text": "Wednesday, June 6-June 27, 2018\n12:00-2:15 PM, Eastern Time\n\u00a0\nCOED\n\u00a0\nBible Study Leader:\u00a0Nicki Cornett\n\u00a0\nContact:NickiCornett#gmail.com\n\u00a0\nGenesis Part 4 -\u00a0Wrestling with God - A Study on Isaac, Jacob, and Esau- Precept Workbook (NASB)\n\u00a0\nGod renews His covenant promise with Abraham through Isaac and Jacob.
Here is how i went about decoding - (NOTE - "description" is not here yet because i having an issue working into the events array to access name & descritption
struct Eventbrite: Decodable {
private enum CodingKeys : String, CodingKey { case events = "events", name = "name"}
let events: [String:[String]]
let name: [String:[String]]
}
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else {return}
do {
let eventbriteData = try JSONDecoder().decode(Eventbrite.self, from: data)
print(eventbriteData.name)
name is clearly not in the scope of pagination and events (note the {}) and is a regular [String:String] dictionary which can be decoded into another struct.
Decode this (as description is incomplete I left it out), you don't need CodingKeys:
struct Eventbrite: Decodable {
let events: [Event]
}
struct Event: Decodable {
let name: Name
// let description: [String:String]
}
struct Name : Decodable {
let text, html : String
}
Right so Decodable is actually pretty smart in that you really don't need to write any code to do the decoding yourself. You just have to make sure you match the JSON structure (and make structs that also conform to Decodable for any nested objects). In other words, instead of having variables as dictionaries, make them their own Decodable struct.
So for example:
struct EventBrite: Decodable {
let pagination: Pagination
let events: [Event]
}
struct Pagination: Decodable {
let page_number: Int
let page_size: Int
let continuation: String
let has_more_items: Bool
}
struct Event: Decodable {
let name: EventName
let description: EventDescription
}
struct EventName: Decodable {
let name: String
let html: String
}
etc...
Something else that's important here is if a key or property is not guaranteed to be returned (like let's say that the EventName doesn't always have an html value that comes back from the server you can easily just mark that value as optional. So something like:
struct EventName: Decodable {
let name: String
let html: String?
}
Another side note, you actually messed up your dictionary type declarations. You'll notice that event is actually of type [String: [String: String]] since the key is a string and the values seem to always be dictionary. And name is [String: String]. Which is not what you had them down as in your original question.
When the values can be different like with pagination you'll want to do something like [String: Any] so just be careful about that.
HOWEVER The approach I suggested I think is better than having properties be dictionaries. For one you don't have to worry about declaring the type of dictionary it is (which you made some small errors on). But more importantly when each dictionary just becomes its own clearly defined struct and you don't have to worry about remembering or looking up the keys. Dot syntax/auto complete will automatically tell you what there can be! (And no casting when your value is of type Any or AnyObject!)
Also definitely use structs for all these as I once benchmarked performance and measured structs efficiency on the order of magnitude of millions of times more efficient than classes. Just a FYI.