Deserializing JSON to KeyValuePairs<string, CustomClass> [duplicate] - json

I have a simple JSON object:
{
"values": {
"a":"",
"b":"",
"c":"",
"d":"",
"e":""
}
}
and I want to decode it to a Swift struct in such a way, that I can later be able to iterate over the keys in values in the exact same order as I receive the JSON object.
Is this possible in Swift?
My try below:
let json = "{ \"values\": { \"a\":\"\", \"b\":\"\", \"c\":\"\", \"d\":\"\", \"e\":\"\" } }"
struct JSS: Codable {
var values: [String: String?]?
}
let data = json.data(using: .utf8)
do {
let decoder = JSONDecoder()
let jss = try decoder.decode(JSS.self, from: data!)
jss.values?.map { print("\($0.key)") }
}
catch {
}
will print:
b
e
a
d
c

This is not a Swift limitation per se. Both Swift and JSON Dictionaries are unordered. The JSON format does not guarantee key ordering, and as such, does not require parsers to preserve the order.
If you need an ordered collection, you'd be better off with returning an array of key-value pairs in the JSON:
{
"values": [
{"a" : ""},
{"b" : ""},
{"c" : ""},
{"d" : ""},
{"e" : ""}
]
}
And then store the keys in the right order to be able to iterate over them as you wish.

The following does not work so you don't waste time trying:
Back to the old fashion way, use JSONSerialization.jsonObject(with: Data, options: JSONSerialization.ReadingOptions) -> Any.
It does keep order..... UNTIL you cast json?["values"] into a [String: Any]. At this time what Cezar says in the above answer enters the scene: dictionaries are unordered.
The following screenshot shows that, until json?["values"] is an Any, the order is kept in the string description.

Related

I expect the result to be a dictionary but it is accessed like a property... why?

I have this JSON coming from a server...
{
"cars": [
{
"name": "Ferrari",
"price": "100"
},
{
"name": "Lamborghini",
"price": "200"
},
{
"name": "Ford Pinto",
"price": "1"
}
]
}
This JSON is a dictionary called cars that contains an array of cars, right?
Then I have this struct...
struct Cars: Codable {
let cars: [Car]
}
struct Car: Codable, Hashable, Identifiable {
let id = UUID()
let name: String
let price: String
}
and I decode the JSON using this:
let (data, _) = try await urlSession.data(from: url)
let result = try JSONDecoder().decode(Cars.self, from: data)
let listOfCars = result.cars
This is something I don't understand.
in result.cars, cars is a property of result that was declared as an array in the struct Cars. Not a dictionary.
I was expecting to access it using result["cars"].
Why is that?
In your code here...
let (data, _) = try await urlSession.data(from: url)
let result = try JSONDecoder().decode(Cars.self, from: data)
let listOfCars = result.cars
result is an instance of the Struct Cars. Your set up code has told Swift how to translate from a JSON dictionary into your own Struct.
So everything inside of result is accessed just like how you would access it in the following...
let result = Cars(cars: [
Car(name: "Ford", price: "£10,000")
])
print(result.cars)
The only difference is how you are creating it. Instead of using the init method like this you are using a JSON decode to decode some JSON into your custom type.
As said in the comments and answers, it takes a result type according to your decode strategy. In your code result type is Cars not a dictionary. So you access the properties with using result.cars
If you want something as dictionary instead, you need to decode it like
let result = try decode.decode([String : [Car]].self, from: data)
Now you can access them like a dictionar
print(result["cars"]?.first?.name) // Optional("Ferrari")

JSON Parsing using Decodable protocol

I have json below for which I want to parse/assign values from
{
"Rooms":[
{
"id":"100",
"title":"CS Classroom",
"description":"Classroom for Computer science students",
"capacity":"50"
},
{
"id":"101",
"title":"Mechanical Lab",
"description":"Mechanical Lab work",
"capacity":"50"
},
{
"id":"108",
"title":"Computer Lab",
"description":"Computer Lab work",
"capacity":"50"
}
]
}
This json is of type [Dictionary: Dictonary] which has only key "Rooms"
While creating struct should I create
struct RoomsInfo: Decodable {
let rooms: Rooms
}
struct Rooms {
let id: String
let title: String
let description: String
let capacity: String
}
My 1st Question is: Since I have only Rooms key , Is there a possiblity to create just one struct instead of two ?
My 2nd Question is: What if my json has keys as "Rooms1", "Rooms2", "Rooms3", "Rooms4"... in this case can i create structure which confirms to decodable or do i need to parse it manually?
Please advice
For the first question, you have a key called Room so it has to decode that key,
is it possible to not have it sure, instead of parsing that JSON data first call out the value of that key JSON["Rooms"], and parse what inside as a [Room].self ,
For the second question if the count is unlimited, as if you don't know how much Room key count are going to be, the Decoder abilities are limited then, however you can always map out the values as Dictionary and then decode the values as Room without caring about the key, this trick will do but you will abandon the original Key.
Update for the second case:
Check out this code below.
typealias jsonDictionary = [String: Any]
let jsonData = json.data(using: .utf8)! // converting test json string to data
var arrayOfRooms: [Room] = []
do {
let serialized = try JSONSerialization.jsonObject(with: jsonData, options: []) // serializing jsonData to json object
if let objects = serialized as? [String: Any] { //casting to dictionary
for key in objects.keys { //looping into the keys rooms (n) number
let rooms = objects[key] // getting those rooms by key
let data = try JSONSerialization.data(withJSONObject: rooms!, options: []) //converting those objects to data again to parse
var myRoom = try! JSONDecoder().decode([Room].self, from: data) // decoding each array of rooms
arrayOfRooms.append(contentsOf: myRoom) // appending rooms to main rooms array declared on top
print("Data", data) // just to check
}
print("MY Array Of Rooms Count \(arrayOfRooms.count)")
} else {
print("nil")
}
} catch {
}
Answer #1: Yes, it's possible with nestedContainers but the effort is greater than the benefit.
Answer #2: Decode the dictionary as [String:Room] or use custom coding keys described in this answer

Freddy JSON - copying the collection into a new array

In Freddy JSON with Swift, an extension exists to...
"Create an instance by copying each element of the collection into a new Array", as defined in the Freddy notes below...
public init<Collection>(_ collection: Collection) where Collection : Collection, Collection.Element == Freddy.JSON
// Create an instance initialized with `elements`.
public init(arrayLiteral elements: Freddy.JSON...)
Given that I already have the data, how is this initializer used? Transforming the data in the usual way as below is fine, but I am not sure on the syntax to copy the data as a collection.
let json = try JSON(data: data)
If it helps, my intention is to get the content of each array and create a new object from it.
[{
"array1": [{
"array1keys": "example",
}],
"array2": [{
"array2keys": "example"
}]
}]
Why don't you use the Codable protocol provided out of the box by Swift?
Given your json
let data = """
[
{
"array1": [
{
"array1keys": "example",
}
],
"array2": [
{
"array2keys": "example"
}
]
}
]
""".data(using: .utf8)!
We can define a type representing your data
typealias SubElement = [String:String]
typealias Element = [String:[SubElement]]
typealias Response = [Element]
And finally we can decode your json
if let response = try? JSONDecoder().decode(Response.self, from: data) {
print(response)
}
[["array1": [["array1keys": "example"]], "array2": [["array2keys": "example"]]]]
Usually I would suggest to define a struct conform to Codable which makes the code much more easy to read. But in this case this is not possible since there are no fields with a fixed name.

parsing JSON with a decodable?

I have a JSON file:
{
"name": "Jens",
"time": "11.45",
"date": "2018:04:17",
"differentTimestamps":[""]
"aWholeLotOfnames":{
"name1": "Karl"
"name2": "pär"
}
How to parse above JSON ? I have checked this tutorial https://www.youtube.com/watch?v=YY3bTxgxWss. One text tutorial to but i don't get how to make a variable that can take a
"nameOfVar"{}
If it's not a dictionary. The tutorial are using a var nameOfVar: [what should be here in this case] for one that nearly looks like it. The thing is though that theirs are starting with a [{ and ends with a }] while mine only starts with a {? i don't know how to solve this?
Creating corresponding Swift data types for JSON is very easy.
A dictionary {} can be decoded into a class / struct where the keys become properties / members.
An array [] can be decoded into an array of the given (decodable) type.
Any value in double quotes is String even "12" or "false".
Numeric floating point values are Double, integer values are Int and true / false is Bool
null is nil
let jsonString = """
{
"name": "Jens",
"time": "11.45",
"date": "2018:04:17",
"differentTimestamps":[""],
"aWholeLotOfnames":{
"name1": "Karl",
"name2": "pär"
}
}
"""
struct Item: Decodable {
let name, time, date: String
let differentTimestamps: [String]
let aWholeLotOfnames: AWholeLotOfnames
}
struct AWholeLotOfnames : Decodable {
let name1, name2 : String
}
let data = Data(jsonString.utf8)
do {
let result = try JSONDecoder().decode(Item.self, from: data)
print(result)
} catch { print(error) }

Parsing JSON using codable and ignoring first layer of JSON

I have JSON like this:
{
"success": true,
"message": "",
"result": {
"buy": [
{
"Quantity": 0.0056,
"Rate": 18527
},
{
"Quantity": 0.11431426,
"Rate": 18526
}
],
"sell":[
{
"Quantity": 8.20604116,
"Rate": 18540
},
{
"Quantity": 0.95600491,
"Rate": 18574.99999998
}
]
}
}
and another set of JSON like this:
{
"lastUpdateId": 1027024,
"bids": [
[
"4.00000000", // PRICE
"431.00000000", // QTY
[] // Can be ignored
]
],
"asks": [
[
"4.00000200",
"12.00000000",
[]
]
]
}
What is the best way to parse these two responses using codable. They both need to be parsed using the same struct or need to be converted to the same struct (whatever will do the job faster). I don't want to create a struct for the entire first response because I am not going to use keys like "success" and "message". I basically want to ignore those and get directly to the "result" key But in the second response, I will being using all the data so I have created a struct for that called TotalOrderBook. What is the best way to do this?
What is confusing me is ignoring the keys "success" and "message" in the first JSON response and getting straight to the value for the key "result". Is it possible to do that without creating an additional struct?
This is what I have right now. I would like to avoid adding another struct since the only thing I really need is the values under buy/bid and sell/sell.
struct TotalOrderBook:Codable{
var buy:[UniversalOrder]?
var sell:[UniversalOrder]?
var bid:[UniversalOrder]?
var ask:[UniversalOrder]?
var buyOrderBook: [UniversalOrder] {
return bid ?? buy ?? [UniversalOrder]()
}
var sellOrderBook: [UniversalOrder] {
return ask ?? sell ?? [UniversalOrder]()
}
var updatedTime:Date
}
struct UniversalOrder:Codable{
var price : Double {
return Double(rate ?? binPrice ?? 0)
}
var size : Double {
return Double(quantity ?? binQuantity ?? 0 )
}
//let numOrders : Int
var quantity:Double?
var rate:Double?
var binPrice:Double?
var binQuantity:Double?
private enum CodingKeys: String, CodingKey {
case rate = "Rate"
case quantity = "Quantity"
//case numOrders, binPrice,
}
init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
binPrice = Double(try container.decode(String.self)) ?? nil
binQuantity = Double(try container.decode(String.self)) ?? nil
quantity = nil
rate = nil
}
}
This is how I am decoding:
let decoder = JSONDecoder()
let data = try! JSONSerialization.data(withJSONObject: value) //value is the response from Alamofire
var theWholeOrderBook:UniversalOrder!
do {
theWholeOrderBook = try decoder.decode(UniversalOrder.self, from: data)
} catch let error {
//print ("error is \(e) ** \(value)")
}
To answer your questions directly, yes it is very easy to ignore the success and message key-value pairs and head straight to results.
Despite this it will be a bit complicated to have a single struct to parse both of these JSON responses.
Both of them have a very different structure which will make it easier to use two different structs to use encoding. To highlight some differences :
buy, sell are nested inside results. bids, asks aren't.
The keys are completely different.
buy, sell have an array of key-value pairs while bids, asks simple have an array of values.
Codable structs should be simple and clear. It's better to have two of those corresponding to each response.