appending optional elements to an array from json in Swift - json

I have just started getting familiar with JSON. I have a class object that I am initialising by passing in a JSON. This object has an array that may be empty and I need to check it for value. so far I am trying:
init(json: JSON) {
id = json["id"].string
type = json["type"].string
text = json["text"].string
answer = json["answer"].string
format = json["format"].string
answeredBy = []
if let answeredByjson = json["answeredBy"].array {
for (_, userDict) in answeredByjson {
if let userDict = userDict as? [String : Any] {
answeredBy.append(JSON(User(dictionary: userDict)))
}
}
}
}
the elements in the array are dictionaries that have to be used to initialize another object (User).
the error I am getting is:
Expression type '[JSON]' is ambiguous without more context.
How can I update my code?
this is my json:
{
"answer" : "rachel",
"answeredBy" : {
"j1K4WXbOFRXfm3srk9oMtZJ8Iop2" : {
"email" : "an email",
"pictureURL" : "a URL",
"uid" : "j1K4WXbOFRXfm3srk9oMtZJ8Iop2",
"name" : "a name"
},
"vtYlmyerugedizHyOW6TV847Be23" : {
"email" : "an email",
"pictureURL" : "a URL",
"uid" : "vtYlmyerugedizHyOW6TV847Be23",
"name" : "Rusty Shakleford"
}
},
"format" : "field",
"id" : "1",
"type" : "text",
"text" : "In a foot race, Jerry was neither first nor last. Janet beat Jerry, Jerry beat Pat. Charlie was neither first nor last. Charlie beat Rachel. Pat beat Charlie. Who came in last?"
}

if let answeredByjson = json["answeredBy"].array {
...
}
produces an array [JSON], but you expect it to produce a dictionary [String: JSON]. That is why you receive:
Expression type '[JSON]' is ambiguous without more context
To produce a dictionary:
if let answeredByjson = json["answeredBy"].dictionary {
...
}
It you don't expect it to produce a dictionary [String: JSON] then this line doesn't make sense
for (_, userDict) in answeredByjson {

Related

How to retrieve all key-value pairs avoiding key duplication from JSON in Groovy script

I am totally new to groovy script and would like some help to solve this out. I have a JSON response I want to manipulate and get desired parameters back by avoiding duplication. The Json response does not have indexes like 0,1,2.. that I can iterate through.
Here is the response that I want to work with:
{
"AuthenticateV2" : {
"displayName" : "Verification of authentication",
"description" : "notification ",
"smsTemplate" : "authentication.v2.0_sms",
"emailHeaderTemplate" : "v2.0_header",
"emailBodyTemplate" : "html",
"parameters" : {
"displayName" : "USER_DISPLAY_NAME",
"actionTokenURL" : "VERIFICATION_LINK",
"customToken" : "VERIFICATION_CODE"
},
"supportedPlans" : [
"connectGo"
]
},
"PasswordRecovery" : {
"displayName" : "Verification of password recovery",
"description" : "notification",
"smsTemplate" : "recovery.v1.0_sms",
"emailHeaderTemplate" : "recovery.v1.0_header",
"emailBodyTemplate" : "recovery.v1.0_body_html",
"parameters" : {
"displayName" : "USER_DISPLAY_NAME",
"actionTokenURL" : "VERIFICATION_LINK",
"customToken" : "VERIFICATION_CODE",
"adminInitiated" : false,
"authnId" : "AUTHENTICATION_IDENTIFIER",
"authnType" : "EMAIL",
"user" : {
"displayName" : "USER_DISPLAY_NAME"
}
},
"supportedPlans" : [
"connectGo"
]
},
"PasswordReset" : {
"displayName" : "password reset",
"description" : "notification",
"smsTemplate" : "recovery.v1.0_sms",
"emailHeaderTemplate" : "recovery.v1.0_header",
"emailBodyTemplate" : "html",
"parameters" : {
"displayName" : "USER_DISPLAY_NAME",
"user" : {
"displayName" : "USER_DISPLAY_NAME"
}
}
The expected output that I want to have:
{
"displayName" : "USER_DISPLAY_NAME",
"actionTokenURL" : "VERIFICATION_LINK",
"customToken" : "VERIFICATION_CODE",
"customToken" : "VERIFICATION_CODE",
"adminInitiated" : false,
"authnId" : "AUTHENTICATION_IDENTIFIER",
"authnType" : "EMAIL"
}
I need to retrieve all fields under parameters tag and also want to avoid duplication
You should first get familiar with parsing and producing JSON in Groovy.
Then, assuming the provided response is a valid JSON (it's not - there are 2 closing curlies (}) missing at the end) to get all the parameters keys merged into one JSON we have to convert the JSON string into a Map object first using JsonSlurper:
def validJsonResponse = '<your valid JSON string>'
Map parsedResponse = new JsonSlurper().parseText(validJsonResponse) as Map
Now, when we have a parsedResponse map we can iterate over all the root items in the response and transform them into the desired form (which is all the unique parameters keys) using Map::collectEntries method:
Map uniqueParameters = parsedResponse.collectEntries { it.value['parameters'] }
Finally, we can convert the uniqueParameters result back into a pretty printed JSON string using JsonOuput:
println JsonOutput.prettyPrint(JsonOutput.toJson(uniqueParameters))
After applying all the above we'll get the output
{
"displayName": "USER_DISPLAY_NAME",
"actionTokenURL": "VERIFICATION_LINK",
"customToken": "VERIFICATION_CODE",
"adminInitiated": false,
"authnId": "AUTHENTICATION_IDENTIFIER",
"authnType": "EMAIL",
"user": {
"displayName": "USER_DISPLAY_NAME"
}
}
If you want to get rid of user entry from the final output just remove it from the resulting uniqueParameters map (uniqueParameters.remove('user')) before converting it back to JSON string.

F# Define Types for Json with nested list

Follow up question to this question, to parse json to deedle dataframe.
My json includes some list with nested lists:
apiResponse = {"data":
{"Shipments":
{"ErrorMessage":null,
"Success":true,
"ValidationResult":null,
"TotalCount":494,
"Data":[
{
"CompletedDate":"2021-04-12T10:13:13.436Z",
"ItemId":"dd4520bb-aa0a-...",
"Organization":{
"OrganizationId":"cac43a32-1f08-...",
"OrganizationName":"XXX"
},
"ShipmentContent" : [
{
"MaterialId": "cac43a32-1f08-..",
"unit": "kg",
"quantity": 2.0,
"labels" : ["A1", "A2", "A3"]
},
{
"MaterialId": "cac43333-1f08-",
"unit": "t",
"quantity": 0.4,
"labels" : ["B1", "B2", "B3"]
}
]
},
{......,
....}]}}}
My code so far:
type ApiTypes =
{
ItemId : Guid
CompletedDate : Nullable<DateTime>
Organization:
{|
OrganizationId : Guid
OrganizationName : string
|}
ShipmentContent : // ? How to define the List
{|
MaterialId : Guid
Quantity : Nullable<decimal>
Unit : string
Labels: List // ?
|}
}
How can I define these types? The next step would be:
let jsonObject = JObject.Parse(graphResponse)
let frame =
jsonObject.["data"].["Shipments"].["Data"].Children()
|> Seq.map (fun jtok -> jtok.ToObject<ApiTypes>())
|> Frame.ofRecords
Can I define all in one type, or do I have to define a seperate type for ShipmentContents?
I realy apreciate your help!
Fiddle here (doesn't work, but for sharing the code)
This should work if you want to have it all in one type:
type ApiTypes =
{
ItemId : Guid
CompletedDate : Nullable<DateTime>
Organization:
{|
OrganizationId : Guid
OrganizationName : string
|}
ShipmentContent :
{|
MaterialId : Guid
Quantity : Nullable<decimal>
Unit : string
Labels: string[]
|}[]
}
Note that I've defined the lists as arrays, since arrays are the collection type supported by JSON.

JSONDecoder changing order of elements [duplicate]

I am trying to create a dictionary that I can make into a JSON formatted object and send to the server.
Example:
var users = [
[
"First": "Albert",
"Last": "Einstein",
"Address":[
"Street": "112 Mercer Street",
"City": "Princeton"]
],
[
"First": "Marie",
"Last": "Curie",
"Address":[
"Street": "108 boulevard Kellermann",
"City": "Paris"]]
]
I use this function
func nsobjectToJSON(swiftObject: NSObject) -> NSString {
var jsonCreationError: NSError?
let jsonData: NSData = NSJSONSerialization.dataWithJSONObject(swiftObject, options: NSJSONWritingOptions.PrettyPrinted, error: &jsonCreationError)!
var strJSON = NSString()
if jsonCreationError != nil {
println("Errors: \(jsonCreationError)")
}
else {
// everything is fine and we have our json stored as an NSData object. We can convert into NSString
strJSON = NSString(data: jsonData, encoding: NSUTF8StringEncoding)!
println("\(strJSON)")
}
return strJSON
}
But my result is this:
[
{
"First" : "Albert",
"Address" : {
"Street" : "112 Mercer Street",
"City" : "Princeton"
},
"Last" : "Einstein"
},
{
"First" : "Marie",
"Address" : {
"Street" : "108 boulevard Kellermann",
"City" : "Paris"
},
"Last" : "Curie"
}
]
Problem: why is the last name last? I think it should be above address. Please let me know what I am doing wrong with the NSDictionary for this to come out wrong. Any help would be very much appreciated - thank you.
To post what has already been said in comments: Dictionaries are "unordered collections". They do not have any order at all to their key/value pairs. Period.
If you want an ordered collection, use something other than a dictionary. (an array of single-item dictionaries is one way to do it.) You can also write code that loads a dictionary's keys into a mutable array, sorts the array, then uses the sorted array of keys to fetch key/value pairs in the desired order.
You could also create your own collection type that uses strings as indexes and keeps the items in sorted order. Swift makes that straightforward, although it would be computationally expensive.
I did like this.
let stagesDict = NSDictionary()
if let strVal = sleepItemDict["stages"] as? NSDictionary {
stagesDict = strVal
let sortedKeys = (stagesDict.allKeys as! [String]).sorted(by: <)
var sortedValues : [Int] = []
for key in sortedKeys {
let value = stagesDict[key]!
print("\(key): \(value)")
sortedValues.append(value as! Int)
}
}

My JSON parsing fails

Im using SwiftyJSON, Alamofire_SwiftyJSON and Alamofire libraries. I'm trying to parse my response but it returns nothing("definitions" fails to print).
JSON isn't empty it has got the response.
So, my code looks like the following (nothing fails, it all compiles)
Alamofire.request(request).responseSwiftyJSON { dataResponse in
if let JSON = dataResponse.result.value {
print(JSON)
if let definitions = JSON["results"]["lexicalEntries"]["entries"]["senses"]["definitions"].string {
print(definitions)
print("Hello")
}}
}
My response model looks like (this is not the whole response, that's just what I want to reach :
{
"results" : [
{
"language" : "en",
"id" : "ace",
"type" : "headword",
"lexicalEntries" : [
{
"language" : "en",
"entries" : [
{
"etymologies" : [
"Middle English (denoting the ‘one’ on dice): via Old French from Latin as ‘unity, a unit’"
],
"grammaticalFeatures" : [
{
"type" : "Number",
"text" : "Singular"
}
],
"homographNumber" : "000",
"senses" : [
{
"definitions" : [
"a playing card with a single spot on it, ranked as the highest card in its suit in most card games"
I think that my problem is in, should I add any parentheses or any symbols?
if let definitions = JSON["results"]["lexicalEntries"]["entries"]["senses"]["definitions"].string
You are not indexing the corresponding arrays in your JSON response, to access the definitions array you can simply use
JSON['results'][0]['lexicalEntries'][0]['entries'][0]['senses'][0]['definitions']

Parsing document fragments with lift-json

I'm trying to parse a JSON document with lift-json when I may not know the exact structure and order of the document that I'm parsing. The document contains list of "objects", all organized into sections for that object type with each section named for that type. I've played around with various ways to loop over the types, pattern-matching on the type name and then trying to get that list of objects out but it never seems to work properly. I either get a blank list or an error about not being able to find the proper JSON chunk to map to my case classes.
Here's some (almost pseudo) code that is as close as I've come:
case class TypesQueries(queries: Map[String, JValue]);
case class AddressQueries(addresses: List[AddressQuery]);
case class AddressQuery(street: String, city: String, state: String, zip: Int)
case class NameQueries(names: List[NameQuery]);
case class NameQuery(firstName: String, lastName: String);
case class EmailQueries(emails: List[EmailQuery]);
case class EmailQuery(emailAddress: String);
val jsonData = parse("""{
"queries" : {
"addresses" : [
{
"street" : "1234 Main St.",
"city" : "New York",
"state" : "New York",
"zip" : 12345
},
{
"street" : "9876 Broadway Blvd.",
"city" : "Chicago",
"state" : "IL",
"zip" : 23456
}
],
"names": [
{
"firstName" : "John",
"lastName" : "Doe"
}
],
"emails" : [
{
"emailAddress" : "john.doe#gmail.com"
},
{
"emailAddress" : "david.smith#gmail.com"
}
]
}
}""");
val typesQuery = parse(jsonData).extract[TypesQueries];
typesQuery.queries.foreach { case(queryType, queryDefinition) =>
queryType match {
case "addresses" =>
// These extract methods do not work.
val addressQueries = queryDefinition.extract[AddressQueries];
case "names" =>
// These extract methods do not work.
val nameQueries = queryDefinition.extract[NameQueries];
case "emails" =>
// These extract methods do not work.
val emailQueries = queryDefinition.extract[EmailQueries];
}
}
"addresses", "names" and "email" may come in any order inside "queries" and there may be a variable number of them.
In the end, I want to be able to extract lists of objects for the respective list of types and then, once the parsing is complete, pass the various lists of objects to the appropriate method.
So, the question is: How can I parse into case classes in lift-json if I do not know what the complete document structure will be ahead of time.
You were very close, this works on the repl:
(Updated)
typesQuery.queries.foreach {
case(queryType, queryDefinition) => queryType match {
case "addresses" => val addressQueries = typesQuery.queries.extract[AddressQueries]; println(addressQueries)
case "names" => val nameQueries = typesQuery.queries.extract[NameQueries]; println(nameQueries)
case "emails" => val emailQueries = typesQuery.queries.extract[EmailQueries]; println(emailQueries)
}
}
The idea is that the foreach "removes" the list that encloses each "object", so we call typesQuery.queries.extract to help the case classes match our parsed json