Building JSON dynamically in Swift - json

I'm trying to build some JSON in Swift. I've seen some examples of turning a Dictionary into JSON or a class into JSON, but I want to be able to turn data into JSON on the fly. I also have multiple objects I want to store, not just one (which most examples I find seem to do).
Here is my code that creates dictionaries of the objects I want to store:
for topic in topics{
var relEntries = getRelevantEntries(topic.id)
var entryItem:[String:Any] = ["":""]
for entry in relEntries{
var eId = ["id":entry.id]
var eTitle = ["title":entry.title]
entryItem = [
"id":entry.id,
"title":entry.title
]
}
var topicItem:[String:Any] = [
"id":topic.id,
"topicName":topic.name,
"entries":entryItem
]
}
I want to create a JSON Array, where each object includes an int, string, and another JSON Array. I wrote out a demo table to clarify.
[
{ id: "id",
topicName: "topicName",
entries:[
{ id: "id", title: "title" },
{ id: "id2", title: "title2"}
]},
{ id: "id2",
topicName: "topicName2",
entries:[
{ id: "id", title: "title" },
{ id: "id2", title: "title2"}
]}
]
So where do I go from here to combine all my topics into one JSON Array? Thanks.

You can use serialization techniques on your custom objects to build Foundation types that represent them.
After that, you can use the NSJSONSerialization class to generate the json.
Since you have to differentiate between your different classes, there is less except handling with mirrors that might generalize that work for you at this point in time, afaik.

You say:
I want to create a JSON Array, where each object includes an int, string, and another JSON Array. I wrote out a demo table to clarify.
[
{ id: "id",
topicName: "topicName",
entries:[
{ id: "id", title: "title" },
{ id: "id2", title: "title2"}
]},
{ id: "id2",
topicName: "topicName2",
entries:[
{ id: "id", title: "title" },
{ id: "id2", title: "title2"}
]}
]
Well, what is that? It's an array ([...]) of dictionaries ({...}), where each dictionary has an id key (with a string value), a topicName key (with a string value), and an entries key, where each entries value is an array containing two dictionaries with an id key and a title key (each with a string value).
So your job is merely to construct that structure and serialize it into JSON. That's trivial. Here, as a mockup, I'll demonstrate using literals:
var d1 : [NSObject:AnyObject] = ["id":"id", "topicName":"topicName"]
var d2 : [NSObject:AnyObject] = ["id":"id2", "topicName":"topicName2"]
let dd1 = ["id":"id", "title":"title"]
let dd2 = ["id":"id2", "title":"title2"]
let arr1 = [dd1,dd2]
let arr2 = [dd1,dd2]
d1["entries"] = arr1
d2["entries"] = arr2
let arr = [d1,d2]
let d = try! NSJSONSerialization.dataWithJSONObject(arr, options: [])
let s = NSString(data: d, encoding: NSUTF8StringEncoding)
print(s!)
Output:
[
{"id":"id",
"entries":[
{"id":"id","title":"title"},
{"id":"id2","title":"title2"}
],
"topicName":"topicName"
},
{"id":"id2",
"entries":[
{"id":"id","title":"title"},
{"id":"id2","title":"title2"}
],
"topicName":"topicName2"
}
]
I believe you'll find that that's exactly the equivalent of what you asked for. Now all you have to do is write code that converts your original data structure into the data structure that I constructed above out of literals.

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

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.

Read JSON Data With String Key: Angular2

I am trying to iterate json data in angular2.
If JSON Data is like this
{fileName: "XYZ"}
I am able to iterate using- let data of datas
But If my JSON data key is in string format, how I can iterate in angular2?
{"fileName": "XYZ"}
JSON always have double quoted string keys, so these:
{ fileName: "XYZ" }
{ 'fileName': "XYZ" }
Are not valid jsons, but this is:
{ "fileName": "XYZ" }
Javascript objects don't require the keys to be quoted, and if they are then a single quote can be used:
let a = { fileName: "XYZ" };
let b = { 'fileName': "XYZ" };
let c = { "fileName": "XYZ" };
Here a, b and c are equivalent.
In any case, iterating all of those js object is done in the same way:
for (let key in a) {
console.log(`${ key }: ${ a[key] }`);
}
Object.keys(b).forEach(key => console.log(`${ key }: ${ b[key] }`));