I´m using an JSON api to get data and everything is working as it should, but I don´t really like the way I´m handling the result to be safe if it´s nil. This is how it looks today:
I get my JSON and then I iterate it and do the following
let obj = Obj()
obj.name = json["name"].string ?? ""
obj.age = json["age"].string ?? ""
obj.length = json["length"].string ?? ""
So I check if for example json["name"].string has a value otherwise I assign it with an empty "". Is this the way to do it with SwiftyJSON or is there a cleaner way?
The issue is that sometimes json["name"].string has a value but not json["age"].string for example.
What you need to do before you start writing code is to figure out what you actually want to do if a value isn't present, or if it is null. You apparently decided that you replace all values that are absent with empty strings. That means you lose the ability to distinguish between the two cases. At some point that will cost you.
I'd add methods that can be called as
jsonString ("name")
jsonStringOrEmpty ("name")
jsonStringOrEmptyAcceptNull ("name")
and so on, using exactly the one that I want to use in each case, and each implemented to give me debugging information when an unexpected input is encountered.
Related
I would like something similar to what node-odata offers, but I do not want to wrap it around my database (I am using Cassandra and already have an Express app set up with routes, etc).
Currently, I grab data from the database (which will ultimately return a JSON object to the user) and then using the values passed in the query string I modify the results with JavaScript and pass the modified JSON object on through to the user.
I cannot pass in a query string like this http://localhost:3001/getSomeData?name=jim&age=21||eyeColor=red which includes logical operators in the query string, and would grab all data and filter it where the name is "jim", the age is "21" OR eyeColor is "red". So this would give me all Jims that have either eyeColor red and/or age of 21. If I used this age=21&&eyeColor=red I would expect to get all Jims that have BOTH eye color of red and are 21 years old.
I was thinking of using a custom query string that can be passed in (i.e. inclusive=age&inclusive=eyeColor appended at the end of the query string) and in Node, I would modify the filter results to treat these properties (age and eyeColor) as if they were passed in with the || OR operator). However, this is quite verbose, and I was hoping there was a library or another simpler implementation out there that solves this problem, or somehow lets me pass in simple logical operators into the query string.
I ended up using this library to achieve what I wanted: https://www.npmjs.com/package/jspath
It's well document and worked perfectly for my situation.
npm i querystringify //or
https://cdnjs.cloudflare.com/ajax/libs/qs/6.7.0/qs.min.js
//it will will return an object
const myObject = Qs.parse(location.search, {ignoreQueryPrefix: true});
//you can use object destructuring.
const {age,eyeColor}=Qs.parse(location.search, {ignoreQueryPrefix: true})
By default, parsing will include "?" too.
{ignoreQueryPrefix: true} this option will omit "?".
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!
I'm designing JSON structure for communication between front-end and backend.
As you can see on the JSON structure. The couldHave node value is empty.
Should I remove this node from the JSON totally?
Or leave it as an empty value.
I can think of if I remove them entirely. The size will be reduced.
"structureNames":{
"List View":"default_related_queue_contracts_list_view"
},
"whereClause":{
"mustHave":[
{
"fieldName":"Status",
"comparison":"eq",
"value":"5"
}
],
"couldHave":[
]
}
Though having empty array[] will not be an issue, apart from the size you have mentioned. But if you don't send this node at all, then in cases where you need to access value of couldHave, will start throwing error like Cannot read value <key> of undefined. So, it's better if you send an empty array.
To handle such cases in client, you will anyway write something to handle this undefined case. Or you will use it something like
var a = whereClause.couldHave || [];
From my experience, leaving an empty node will not cause any problems if your front-end is not trying to parse that empty node ...however, it is always good practice to have at least a default empty value... that way your front end can call a function handleMissingInformation() when ever an empty node value is referenced.
I'm using webwrite to post to an api. One of the field names in the json object I'm trying to setup for posting is odata.metadata. I'm making a struct that looks like this for the json object:
json = struct('odata.metadata', metadata, 'odata.type', type, 'Name', name,);
But I get an error
Error using struct
Invalid field name "odata.metadata"
Here's the json object I'm trying to use in Matlab. All strings for simplicity:
{
"odata.metadata": "https://website.com#Element",
"odata.type": "Blah.Blah.This.That",
"Name": "My Object"
}
Is there a way to submit this json object or is it a lost cause?
Field names are not allowed to have dots in them. The reason why is because this will be confused with accessing another nested structure within the structure itself.
For example, doing json.odata.metadata would be interpreted as json being a struct with a member whose field name is odata where odata has another member whose field name is metadata. This would not be interpreted as a member with the combined field name as odata.metadata. You're going to have to rename the field to something else or change the convention of your field name slightly.
Usually, the convention is to replace dots with underscores. An automated way to take care of this if you're not willing to manually rename the field names yourself is to use a function called matlab.lang.makeValidName that takes in a string and converts it into a valid field name. This function was introduced in R2014a. For older versions, it's called genvarname.
For example:
>> matlab.lang.makeValidName('odata.metadata')
ans =
odata_metadata
As such, either replace all dots with _ to ensure no ambiguities or use matlab.lang.makeValidName or genvarname to take care of this for you.
I would suggest using a a containers.Map instead of a struct to store your data, and then creating your JSON string by iterating over the Map filednames and appending them along with the data to your JSON.
Here's a quick demonstration of what I mean:
%// Prepare the Map and the Data:
metadata = 'https://website.com#Element';
type = 'Blah.Blah.This.That';
name = 'My Object';
example_map = containers.Map({'odata.metadata','odata.type','Name'},...
{metadata,type,name});
%// Convert to JSON:
JSONstr = '{'; %// Initialization
map_keys = keys(example_map);
map_vals = values(example_map);
for ind1 = 1:example_map.Count
JSONstr = [JSONstr '"' map_keys{ind1} '":"' map_vals{ind1} '",'];
end
JSONstr =[JSONstr(1:end-1) '}']; %// Finalization (get rid of the last ',' and close)
Which results in a valid JSON string.
Obviously if your values aren't strings you'll need to convert them using num2str etc.
Another alternative you might want to consider is the JSONlab FEX submission. I saw that its savejson.m is able to accept cell arrays - which can hold any string you like.
Other alternatives may include any of the numerous Java or python JSON libraries which you can call from MATLAB.
I probably shouldn't add this as an answer - but you can have '.' in a struct fieldname...
Before I go further - I do not advocate this and it will almost certainly cause bugs and a lot of trouble down the road... #rayryeng method is a better approach
If your struct is created by a mex function which creates a field that contains a "." -> then you will get what your after.
To create your own test see the Mathworks example and modify accordingly.
(I wont put the full code here to discourage the practice).
If you update the char example and compile to test_mex you get:
>> obj = test_mex
obj =
Doublestuff: [1x100 double]
odata.metadata: 'This is my char'
Note: You can only access your custom field in Matlab using dynamic fieldnames:
obj.('odata.metadata')
You need to use a mex capability to update it...
I've search a ton of articles here and elsewhere via the Googles, plus read a few related docs over at docs.python.org and still stuck.
I am getting a response from an API like below:
{"status":"active","perms":1,"data":{"40332895":{"user_values":{"delta":-203,"value":53.32,"order":42509}}}}
I have no problem printing the 'status' or 'data'. However, all I can grab is the name of the *user_values*, not the date inside them.
Been at it for way to long and was hoping someone could point me in the right direction. Fairly new to Python and if I need to change how I am doing this because it is bad practices or there is an easier way to get the results I am looking for, please let me know.
Code:
import json
import urllib2
url = urllib2.urlopen('http://URLofAPI.com')
json_url = json.load(url)
api_result = json_url
for doc in api_result['data']['40332895']['user_values']:
print doc
outputs:
delta
value
order
what I really want to get is the value of them (i.e.: '-203', '53.32', '42509').
I am basically trying to save that data into a list\dict (individually or separately), then print it with other data. I have tried all sorts of things and cannot manage it. I am sure it's probably something super easy that I missing, but it's driving me nuts. :)
Also, I was really expecting the below to give me '42509', but I get an error:
for doc in api_result['data']['40332895']['user_values']['order']
Thanks in advance!
You're asking for the keys of the user_values dictionary, and getting them. Try this:
uv = api_result['data']['40332895']['user_values']
for doc in uv:
print uv[doc]
In your example api_result['data']['40332895']['user_values'] is a dictionary.
If you iterate over a dictionary you will get the keys. This is the case in your original example and in mgkrebbs answer.
However if you iterate over the .iteritems() (or .items()) of the dictionary you get the (key, value) pairs in a tuple:
uv = api_result['data']['40332895']['user_values']
for key,value in uv.iteritems():
print key, value
If you only need the values, you iterate over .itervalues()
uv = api_result['data']['40332895']['user_values']
for value in uv.itervalues():
print value
Or if you only need the values as a list:
my_list = api_result['data']['40332895']['user_values'].values()
The difference between .itervalues() and .values() is that the former gives you an iterable (an object which returns one value at a time, but does not create the structure in memory), while the latter gives you a list.