I have been teaching myself how to parse JSON using SwiftyJSON and the fab PokeApi.co I thought I was getting the hang of it, until I tried to get an array back of a Pokemon's different types. The JSON returned looks like this:
...
],
"base_experience": 155,
"types": [
{
"slot": 2,
"type": {
"url": "https://pokeapi.co/api/v2/type/3/",
"name": "flying"
}
},
{
"slot": 1,
"type": {
"url": "https://pokeapi.co/api/v2/type/1/",
"name": "normal"
}
}
]
}
I need to grab an array of the value for "name" within "types" and then "type". I tried lots of different suggestions from here, the SwiftyJSON guide on their Github and various other sources, and the only thing I could get to work is this:
let typesArray = json["types"].arrayValue.map({$0["type"]["name"].stringValue})
for item in typesArray {
self.currentPokemon.pokemonTypes.append(item.firstUppercased)
print("Added \(item)")
}
I am happy that I have got it to work, but I am desperately trying to get my head around parsing JSON and this just doesn't make sense to me! Could anybody please explain to me what is going on? Is there a clearer/ more readable way of doing this?
Thank you for your time!
JSON
First things first... I think you need to make sure you really understand JSON before you can wrap your head around what you are doing here. JSON objects are essentially a Key / Value object. The Key is a string and the value can be many things: array of more JSON objects, or even as simple as a string.
Best Practices
So one thing I like to do when I have to parse through JSON for any response is create an enum of strings that will have all of the "Keys" in the json. Note that if you do not assign a calue to any case and the type is String when you do pokemonApiParams.name.rawValue it will automatically return the stringified version of your case!
enum pokemonApiParams: String {
case baseExperience
case types
case slot
case type
case url
case name
}
Now when you are working with your JSON you can use this enum to help make things clearer. This is especially nice if your API response ever changed a key. You could just change the enum value and everything would work! Another reason why this is a "best practice" is it takes out these "magic strings" from your code and keeps everything in one logistical place that you can refer to later!
// This would give you 155 in your example
json[pokemonApiParams.baseExperience.rawValue]
Working through your example
Now to the part you were having trouble understanding.
First let's reconstruct your "types array" piece by piece...
let typesArray = json[pokemonApiParams.types.rawValue]
This guy creates a json object that contains the array of "types" in your response. Basic SwiftyJSON objects are great but you can be a little more specific here to get some more usage, hence why your example uses .arrayValue.
let typesArray = json[pokemonApiParams.types.rawValue].arrayValue
Now we have a JSON array object and we could go through it using various different looping functions. However you use the .map function next. This guy is super useful once you get used to it and use it some more!
Understanding .map
Basically we can map each element in our array to be something else, in this case we are using a short hand to go through the array and get only the "name" attribute of each type. So we are mapping from a [JSON] -> String.
The $0 is the shorthand I was talking about. Imagine this as your index in a for loop. The map function will walk through the entirety of the array and $0 is the object in the array that is being looked at.
Check out the map documentation too!
Putting it all together
Looking at your response each types object has a slot and a type.
So back to your example (but using our enum!):
let typesArray = json[pokemonApiParams.types.rawValue].arrayValue
.map {
$0[pokemonApiParams.type][pokemonApiParams.name.rawValue].stringValue
}
Now your typesArray is an array of strings. Another way to write this (which would be a little clearer and maybe easier to understand) is:
let typesArray = json[pokemonApiParams.types.rawValue].arrayValue
.map { (types: JSON) -> String in
types[pokemonApiParams.type.rawValue][pokemonApiParams.name.rawValue].stringValue
}
This is clearer because we have to specify our object (types) instead of using the $0 shorthand and we specify what we are returning (String).
Hopefully this is all correct and clear!
Happy coding and go catch 'em all!
Related
I'm trying to access the roomName, but so far I am unable. I don't get how to get past the barriar of the info.[long ID with dashes].roomName.
At most I can get back the object of the long id or undefined.
I have tried info[0].roomName. Trying to get the first object in the info and then go on from there. The long id number is also in list.id, I don't know if that can help.
I would have set the info as an array like list is, but this is not my JSON, only one that I am working with.
{
"list":[ IGNORE, can access code here ],
"info":{
"e5eb1ccf-bd45-4d01-8e2a":{
"id":"e5eb1ccf-bd45-4d01-8e2a",
"name":"Lucy",
"roomName":"Arts" <<I need to get to this.
}
}
}
I hope this makes sense, first post and this is just a boiled down version of what I have. Putting in the id number in the info.e5eb1ccf-bd45-4d01-8e2a.roomName breaks after the first -
Once you've parsed the JSON (assuming you even have JSON*) and you have an object, you'd use brackets notation:
var room = theObject.info["e5eb1ccf-bd45-4d01-8e2a"].roomName;
console.log(room); // Arts
* Remember, JSON is a textual notation for data exchange. (More here.) If you're dealing with JavaScript source code, and not dealing with a string, you're not dealing with JSON.
I am working with an API that, frustratingly, has field names that vary for the same value. For example, one API response could look like this:
{
"PersonFirstName": "John",
"PersonLastName": "Smith"
}
while another looks like this:
{
"FirstNm": "John",
"LastNm": "Smith"
}
Suppose then I had a struct which I would like to decode my JSON to. It might look like this:
type Name struct {
FirstName string
LastName string
}
Typically I would just be able to do the following if the API was consistent:
type Name struct {
FirstName string `json:"PersonFirstName"`
LastName string `json:"PersonLastName"`
}
and then use the built-in JSON decoder to build the struct. When there are multiple field values like this, though, I don't know a clean way to decode the JSON into a struct. Any ideas?
Use a map[string]string. That is the equivalent structure in Go. You're looking at those as being structs because their objects, and that's the sensible thing to do. Anytime you see different values for the property names that's your clue that a map is the only reasonable representation of the data in Go.
If you need to normalize/have static typing implement a helper function called NameFromMap(data map[string]string) (*Name, error). Put the switch statement in there to deal with the various values you can have for the keys.
Edit: you could also implement UnmarshalJSON for your type. You would just put this switch statement I allude to there. Here's an example; How do I Unmarshal JSON?
I personally prefer the first method I said because this sort of abstracts a step that I'd rather see called explicitly.
I have this kind of json data. And language am using his jquery
{
"Vitals":[
{
"Bp Systolic":"",
"Bp Diastolic":"",
"Weight":"",
"Height":"",
"BMI":""
}
],
"Lab":[
{
"Lipid_profile":[{
"Total Cholestrol":"",
"TRIGLYCERIDES":""
}],
"blood":[{
"A1C":[{ "sample" }],
"ALBUMIN":"",
"CALCIUM":"",
"Glucose":"",
"POTTASIUM":""
}]
}]
}
I want to access the A1C. What will be the efficient way of doing this. I know we can access it by iterating through the json. I am just looking for the efficient way.
Update:
as you guys said i have tried this, But am getting it as undefined
$.get('sample.json',function(data){
alert(data.toSource()); //all data
alert(data.Lab.blood); //undefined
});
Easiest and cleanest way would be to convert the json-string to an object and then access it with jsonObj.Lab.blood.A1C in any language I would say.
Otherwise you could use regEx to find it or substing to get it but both of these solutions would be ugly if you ask me.
You can parse the response into a JSON object and access the path 'obj.blood.A1C' as Erik and Sasse said, or you can search for the specific string with a regExp like
\"A1C\":\[\{ \"([^\"]*)\"
That will return you the contents of the token A1C value into the first matching result group.
If you tell us the programing language maybe we will be able to be a bit more specific.
I am working on a (.NET) REST API which is returning some JSON data. The consumer of the API is an embedded client. We have been trying to establish the structure of the JSON we will be working with. The format the embedded client wants to use is something I have not seen before in working with JSON. I suggested that it is no "typical" JSON. I was met with the question "Where is 'typical' JSON format documented"?
As an example of JSON I "typically" see:
{
"item" : {
"users": [ ... list of user objects ... ],
"times": [ ... list of time objects ...],
}
}
An example of the non-typical JSON:
{
"item" : [
{
"users": [ ... list of user objects ... ]
},
{
"times": [ ... list of time objects ...]
},
]
}
In the second example, item contains an array of objects, which each contain a property whose value is an array of entities. This is valid JSON. However, I have not encountered another instance of JSON that is structured this way when it is not an arbitrary array of objects but is in fact a set list of properties on the "item" object.
In searching json.org, stackoverflow.com and other places on the interwebs I have not found any guidelines on why the structure of JSON follows the "typical" example above rather than the second example.
Can you provide links to documentation that would provide recommendations for one format or the other above?
Not a link, but just straightforward answer: Items are either indexed (0, 1, 2, ...) or keyed (users, times). No matter what software you use, you can get at indexed or keyed data equally easily and quickly. But not with what you call "non-typical" JSON: To get at the users, I have to iterate through the array and find one dictionary that has a key "users". But there might be two or more dictionaries with that key. So what am I supposed to do then? If you use JSON schema, the "non-typical" JSON is impossible to check. In iOS, in the typical case I write
NSArray* users = itemDict [#"users"];
For the non-typical JSON I have to write
NSArray* users = nil;
for (NSDictionary* dict in itemArray)
if (dict [#"users"] != nil)
users = dict [#"users"];
but that still has no error checking for multiple dicts with the key "users". Which is an error that in the first case isn't even possible. So just tell them what the are asking for is rubbish and creates nothing but unnecessary work. For other software, you probably have the same problems.
I've run into a use case where I need to parse a bunch of information from a txt file. The meat of the payload is a bunch of key:value pairs, none of which I know at development time.
Wombat
Area: "Northern Alberta"
Tank Level (ft): -3.395
Temperature (C): 19.3
Batt Voltage: 13.09
Last Maintained: 2012-01-01
Secured: "Yes"
As you can see, there is potential for Strings, Numbers, Dates and Booleans. There is also a use case where the user needs to create rules for certain attributes, things like:
When the Tank Level exceeds n, please notify some.user#someplace.com
When a Site that contains "Alberta" is Not Secured, please notify some.user#someplace.com
Depending on the type of attribute, the available rule types will differ. I may also need to do some kind of aggregation on the numeric types. Anyway, to make a long story short, I need the type information. So what kind of data structure is best?
Initially I was going to go with distinct tuples.
val stringAttributes: Array[(String, String)]
val doubleAttributes: Array[(String, Double)]
val dateAttributes: Array[(String, Date)]
Now that seems wrong, or at the very least ugly. Then I though maybe something like:
val attributes: Array[(String, Any)]
Now I have a pattern match in many of places. Also note I'm using a JSON protocol for the web application and database (MongoDB). It'd be convenient to give the front end something like this:
{
site: "Wombat",
attributes: [
{ "Area": "Northern Alberta" },
{ "Tank Level (ft)": -3.395 },
{ "Temperature (C)": 19.3 }
]
}
But in the back end, do I encode the types? Do I parse raw JSON? In the end, I'm looking for the best way to maintain type information for dynamic set of attributes while supporting JSON to both the web client and the database.
+1 on #david's comment.
Why not use JSON end-to-end and derive the type desired for each individual rule? Then use a single function to transform a JSON value to the correct type (using defaults or Option when it doesn't really match).
Then you can use something like this to run the ruleset over all of the parsed objects:
for (object <- parseObjects(); rule <- rules) {
val value = coerce(object, rule.desiredType)
rule(value)
}
Or something similar, such as having the rule itself call the coercion code. It seems like you don't really need the objects to have a type until you're doing rule processing, so why try to?
P.S.
Typesafe Config may be able to parse the file for you already, although it looks to be a simple format to parse.