I want to get "key of dictionary" (that's what I called, not sure if it is right name) on this JSON
{
"People": {
"People with nice hair": {
"name": "Peter John",
"age": 12,
"books": [
{
"title": "Breaking Bad",
"release": "2011"
},
{
"title": "Twilight",
"release": "2012"
},
{
"title": "Gone Wild",
"release": "2013"
}
]
},
"People with jacket": {
"name": "Jason Bourne",
"age": 15,
"books": [
{
"title": "Breaking Bad",
"release": "2011"
},
{
"title": "Twilight",
"release": "2012"
},
{
"title": "Gone Wild",
"release": "2013"
}
]
}
}
}
First of all, I already created my People struct that will be used to map from those JSON.
Here is my people struct
struct People {
var peopleLooks:String?
var name:String?
var books = [Book]()
}
And here is my Book struct
struct Book {
var title:String?
var release:String?
}
From that JSON, I created engine with Alamofire and SwiftyJSON that will be called in my controller via completion handler
Alamofire.request(request).responseJSON { response in
if response.result.error == nil {
let json = JSON(response.result.value!)
success(json)
}
}
And here is what I do in my controller
Engine.instance.getPeople(request, success:(JSON?)->void),
success:{ (json) in
// getting all the json object
let jsonRecieve = JSON((json?.dictionaryObject)!)
// get list of people
let peoples = jsonRecieve["People"]
// from here, we try to map people into our struct that I don't know how.
}
My question is, how to map my peoples from jsonRecieve["People"] into my struct?
I want "People with nice hair" as a value of peopleLooks on my People struct. I thought "People with nice hair" is kind of key of dictionary or something, but I don't know how to get that.
Any help would be appreciated. Thank you!
While you iterate through dictionaries, for instance
for peeps in peoples
You can access key with
peeps.0
and value with
peeps.1
You can use key, value loop.
for (key,subJson):(String, JSON) in json["People"] {
// you can use key and subJson here.
}
Related
I need to deserialize ( and later on serialize ) a piece of data that has this type of a structure :
{
"type": "TypeApplication",
"val": {
"con": {
"type": "TypeConstructor",
"val": [
"Builtin",
"Record"
]
},
"arg": {
"type": "RowCons",
"val": {
"label": "953e3dd6-826e-1985-cb99-fd4ed4da754e",
"type": {
"type": "TypeApplication",
"val": {
"con": {
"type": "TypeConstructor",
"val": [
"Builtin",
"List"
]
},
"arg": {
"type": "Element",
"meta": {
"multiLine": true
}
}
},
"system": {
"label": "nullam-senectus-port - Text",
"isBindable": true,
"defaultValue": [
{
"id": "4a05486f-f24d-45f8-ae13-ab05f824d74d",
"type": "String",
"pluginType": "Basic",
"data": {
"value": "Nullam senectus porttitor in eget. Eget rutrum leo interdum."
},
"children": [],
"text": true
}
],
"isUnlinked": false,
"isDefault": false
}
},
"tail": {
"type": "RowCons",
"val": {
"label": "94f603df-d585-b45a-4252-9ec77cf5b13c",
"type": {
"type": "TypeApplication",
"val": {
"con": {
"type": "TypeConstructor",
"val": [
"Builtin",
"List"
]
},
"arg": {
"type": "Element",
"meta": {
"multiLine": true
}
}
},
"system": {
"label": "best-services - Text",
"isBindable": true,
"defaultValue": [
{
"id": "6265ca45-3f69-4844-97e2-c05bbfb9fee5",
"type": "String",
"pluginType": "Basic",
"data": {
"value": "Best Services"
},
"children": [],
"text": true
}
]
}
},
"tail": {
"type": "RowEmpty"
}
}
}
}
}
}
}
I do not know what this data exactly is, but I know this is trying to represent a function/element that takes in values and defaults for those values as parameters/properties.
I want to deserialize it using serde and consequently serialize it too.
I have so far been able to write something that sort of works but not really :
#[skip_serializing_none]
#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(tag = "type", content = "val")]
pub enum WebflowPropDataType {
TypeApplication {
con: Box<WebflowPropDataType>, // Normally Passes the Type Constructor
arg: Box<WebflowPropDataType>, // Normally Passes the Row Constructor
},
TypeConstructor(Vec<String>), // Stores Value of TypeConstructor
RowCons {
label: String, // Stores the label of the Row
#[serde(rename = "type")]
row_con_type: Box<WebflowPropDataType>, // Stores the type of the Row
tail: Box<WebflowPropDataType>,
},
RowEmpty, // For Ending the recursive usage of rowConstructor
}
#[skip_serializing_none]
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct WebflowRowConDataType {
#[serde(rename = "type")]
val_type: String, // TypeApplication
val: Box<WebflowPropDataType>,
}
This works for a structure like this :
{
"type": "TypeApplication",
"val":{
"con": {
"type": "TypeConstructor",
"val": []
},
"arg": {
"type": "RowEmpty"
}
}
}
but would fail if I try to work with initial example. I know this may be due to the lack of a proper arg type or maybe even the TypeApplication Enum hand is malformed.
I do see that a adjancent typing solution would be enough for most of the times but there are cases as seen in the example structure that there is a third value called system and I am unable to determine what type of approach would help me achieve this type of outcome.
How should I approach this problem in order to generate this type of code.
I am not asking anyone to write me a solution but to give me suggestion as to what my approach should be? Whether you'd know what type of data this is/how to generated this , or even if there are some other library I should look into to manipulate this type of data or maybe look at other approaches.
PS : - My end goal is to be able to generate / serialize this type of JSON code from already contained knowledge of properties and default values of a function/object.
Here are my recommendations:
Use just #[serde(tag = "type")] instead of #[serde(tag = "type", content = "val")]. You will have to handle val manually (extracting the current enum members into separate structs), but this allows you to also handle TypeApplication.system and Element.meta.
This also has the small benefit of reducing the amount of Boxes involved.
Consider whether all of the different cases in WebflowPropDataType can actually occur everywhere it's used. If not (maybe Element can only happen under TypeApplication.val.arg), then you may want to split the enum into multiple so that this is reflected in the type system.
Example for #1:
use serde::{Serialize, Deserialize};
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct TypeApplicationVal {
con: WebflowPropDataType, // Normally Passes the Type Constructor
arg: WebflowPropDataType, // Normally Passes the Row Constructor
}
// #[skip_serializing_none]
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct TypeApplicationSystem {
label: String,
#[serde(rename = "isBindable")]
is_bindable: bool,
// TODO: defaultValue
#[serde(rename = "isUnlinked")]
#[serde(skip_serializing_if = "Option::is_none")]
is_unlinked: Option<bool>,
#[serde(rename = "isDefault")]
#[serde(skip_serializing_if = "Option::is_none")]
is_default: Option<bool>,
}
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct RowConsVal {
label: String, // Stores the label of the Row
#[serde(rename = "type")]
typ: WebflowPropDataType, // Stores the type of the Row
tail: WebflowPropDataType,
}
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct ElementMeta {
#[serde(rename = "multiLine")]
multi_line: bool,
}
#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(tag = "type")]
pub enum WebflowPropDataType {
TypeApplication {
val: Box<TypeApplicationVal>,
#[serde(skip_serializing_if = "Option::is_none")]
system: Option<TypeApplicationSystem>,
},
TypeConstructor {
val: Vec<String> // Stores Value of TypeConstructor
},
RowCons {
val: Box<RowConsVal>,
},
Element {
meta: ElementMeta,
},
RowEmpty, // For Ending the recursive usage of rowConstructor
}
playground
hello everyone as i am using payment gateway charge credit api but i am getting error while passing json object to api.and here my original JSON object which pass to charge credit api.
{
"createTransactionRequest": {
"merchantAuthentication": {
"name": "XXXXXX",
"transactionKey": "XXXXXXXX"
},
"refId": "123456",
"transactionRequest": {
"transactionType": "authCaptureTransaction",
"amount": "5",
"payment": {
"creditCard": {
"cardNumber": "5424000000000015",
"expirationDate": "2020-12",
"cardCode": "999"
}
}
}
}
}
this is original json request
but in iOS while making JSON we get below JSON object and sequence of JSON object are change that's why we getting error from api.
{
"createTransactionRequest": {
"merchantAuthentication": {
"name": "XXXXXX",
"transactionKey": "XXXXXXX"
},
"refId": "123456",
"transactionRequest": {
"amount": "5",
"payment": {
"creditCard": {
"cardCode": "999",
"cardNumber": "5424000000000015",
"expirationDate": "2020-12"
}
},
"transactionType": "authCaptureTransaction"
}
}
}
after passing this JSON object to API, we will get bellow error
{
"messages": {
"resultCode": "Error",
"message": [
{
"code": "E00003",
"text": "The element 'transactionRequest' in namespace 'AnetApi/xml/v1/schema/AnetApiSchema.xsd' has invalid child element 'amount' in namespace 'AnetApi/xml/v1/schema/AnetApiSchema.xsd'. List of possible elements expected: 'transactionType' in namespace 'AnetApi/xml/v1/schema/AnetApiSchema.xsd'."
}
]
}
}
here is my code for creating JSON object in app
var dict = Dictionary<String, Any>()
dict=[
"merchantAuthentication": [
"name": "xxxxxxx",
"transactionKey": "xxxxxxx"
],
"refId": "5656",
"transactionRequest": [
"transactionType": "authCaptureTransaction",
"amount": "55",
"payment": [
"creditCard": [
"cardNumber": "4111111111111111",
"expirationDate": "2020-12",
"cardCode": "999"
]
]
]
]
after print this JSON sequence are changed
Have you read the documentation? It's very clear from the linked page that what you're running into is a side effect of their translation of JSON elements to XML elements in the backend, specifically around the ordering of parameters in your JSON request:
A Note Regarding JSON Support
The Authorize.Net API, which is not
based on REST, offers JSON support through a translation of JSON
elements to XML elements. While JSON does not typically require a set
order to the elements in an object, XML requires strict ordering.
Developers using the Authorize.Net API should force the ordering of
elements to match this API Reference.
Their examples below this block also show that their transactionType parameters appear as the first attribute in the transactionRequest object. Tl;dr - move your transactionType parameter up in your transactionRequest object:
{
"createTransactionRequest": {
"merchantAuthentication": {
"name": "XXXXXX",
"transactionKey": "XXXXXXX"
},
"refId": "123456",
"transactionRequest": {
"transactionType": "authCaptureTransaction",
"amount": "5",
"payment": {
"creditCard": {
"cardCode": "999",
"cardNumber": "5424000000000015",
"expirationDate": "2020-12"
}
}
}
}
}
please try with following code.
let merchantSub = ["name": "XXXXXX",
"transactionKey": "XXXXXXX"]
let childCreditCard = ["cardCode": "999",
"cardNumber": "5424000000000015",
"expirationDate": "2020-12"]
let creditCard = ["creditCard":childCreditCard]
let transactionRequest = ["amount": "",
"payment":creditCard,
"transactionType":""] as [String : Any]
let merchantAuthentication = ["merchantAuthentication" :merchantSub,
"refId" : "123456",
"transactionRequest":transactionRequest] as [String : Any]
let param = ["createTransactionRequest" : merchantAuthentication]
if let data = try? JSONSerialization.data(withJSONObject: param, options: .prettyPrinted),
let str = String(data: data, encoding: .utf8) {
print(str)
}
However I recommend to use class but for initial level do like this.
I am trying to create multiple children inside a child. I can currently create this inside my recipe:
{
"RecipeData": {
"recipe": {
"-KjTSH4uPQ152Cr-hDok": {
"name": "Cook rice",
"ID": "-KjTSH4uPQ152Cr-hDok",
}
}
}
}
Using:
let recipe: [String : Any] = ["name" : self.recipe.name,
"ID" : self.recipe.key]
The class of the recipe looks like this:
class Recipe {
var name: String!
var key: String
init(from snapshot: FIRDataSnapshot) {
let snapshotValue = snapshot.value as! [String: Any]
self.name = snapshotValue["name"] as! String
self.key = snapshot.key
}
}
But I now want to create another array of children which would be inside "method" and look something like this.
{
"RecipeData": {
"recipe": {
"-KjTSH4uPQ152Cr-hDok": {
"name": "Cook rice",
"ID": "-KjTSH4uPQ152Cr-hDok",
"method": [
{
"step 1": "instruction 1"
},
{
"step 2": "instruction 2"
},
{
"step 3": "instruction 3"
}
]
}
}
}
}
Edit:
The recipe is updated this way
databaseRef.child("RecipeData").child("recipe").updateChildValues(recipe)
I have looked at Firebase How to update multiple children? which is written in javascript, but not sure how to implement it. Feel free to let me know if there are better questions or examples out there.
You can create multiple children inside of a node just as you have been doing with the "recipe" node. For example:
{
"RecipeData": {
"recipe": {
"-KjTSH4uPQ152Cr-hDok": {
"name": "Cook rice",
"ID": "-KjTSH4uPQ152Cr-hDok",
"method": {
"Step1":"instruction1",
"Step2":"instruction2",
"Step3":"instruction3"
}
}
}
}
This is better practice as you can look up each step by key. Although, as Firebase Database keys are strings ordered lexicographically, it would be better practice to use .childByAutoId() for each step, to ensure all the steps come in order and are unique. I'll just keep using "stepn" for this example though.
If you needed further information inside each step, just make one of the step keys a parent node again:
{
"RecipeData": {
"recipe": {
"-KjTSH4uPQ152Cr-hDok": {
"name": "Cook rice",
"ID": "-KjTSH4uPQ152Cr-hDok",
"method": {
"Step1": {
"SpatulaRequired" : true,
"Temp" : 400
}
}
}
}
}
This can be achieved by by calling .set() on a Dictionary of Dictionaries. For example:
let dict: [String:AnyObject] = ["Method":
["Step1":
["SpatulaRequired":true,
"temp":400],
["Step2":
["SpatulaRequired":false,
"temp":500]
]]
myDatabaseReference.set(dict)
I'm attempting to loop through a JSON array sending data to a struct.
Here's my code that uses SwiftyJSON to return a JSON object:
performAPICall() {
json in
if(json != nil){
print("Here is the JSON:")
print(json["content"]["clients"])
let clients = json["content"]["clients"]
for client in clients {
var thisClient = Client()
thisClient.id = client["id"].string
thisClient.desc = client["desc"].string
thisClient.name = client["name"].string
self.clientArray.append(thisClient)
}
self.tableView.reloadData()
} else {
print("Something went very wrong..,")
}
}
I'm not quite sure why I'm getting "has no subscript" errors on the three strings.
Any help appreciated, thanks.
EDIT: Here's a sample of the JSON
{
"content": {
"clients": [{
"group": "client",
"id": "group_8oefXvIRV4",
"name": "John Doe",
"desc": "John's group."
}, {
"group": "client",
"id": "group_hVqIc1eEsZ",
"name": "Demo Client One",
"desc": "Demo Client One's description! "
}, {
"group": "client",
"id": "group_Yb0vvlscci",
"name": "Demo Client Two",
"desc": "This is Demo Client Two's group"
}]
}
}
You should use array method. Thus, your line
let clients = json["content"]["clients"]
should use array (and unwrap it safely):
guard let clients = json["content"]["clients"].array else {
print("didn't find content/clients")
return
}
// proceed with `for` loop here
I'm using Alamofire to get data from an API. I'm using SwiftyJSON to parse the JSON response. Currently, I'm iterating through product data and want to extract product details and display them in a collection view. The issue is, I can't seem to figure out how to access each product in the array. Here's the array that's returned:
{
"1": {
"product_id": 2982493187,
"merged_status": "TRUE",
"merged": [
{
"id": 2982493187,
"title": "Waves Eclipse Tee in Black",
"handle": "waves-eclipse-tee-in-black",
"published_at": "2015-10-09T21:00:50-07:00",
"published_scope": "global",
}
]
}
},
{
"2": {
"product_id": 2982432131,
"merged_status": "TRUE",
"merged": [
{
"id": 2982432131,
"title": "Waves Eclipse Tee in Off White",
"handle": "waves-eclipse-tee-in-off-white",
"published_at": "2015-10-09T21:00:50-07:00",
"published_scope": "global",
}
]
}
}
For each of these products I want to access the product_id. I'm trying to do so like so: productArray[0]["produdct_id"].int, but it doesn't seem to work. Any ideas?
You do not have an array in your results, but a dictionary with keys of "1", "2", etc.
let product_id = result["1"]["product_id"].int
It seems that the result json is an array of dictionary, so you can access the product_id via
let product_id = result[0]["1"]["product_id"].int
Try this:
let jsonProductsArray = JSON(theRawJSONData).array
for (key, productJSON):(String, JSON) in jsonProductsArray {
print(key);
let product_id = productJSON["product_id"].int
print(product_id)
}