How to handle `null` strings coming from a RESTful API in JSONObject? - java

Here's a service return, that gives us user's profile info:
{
email: 'someone#example.com',
pictureUrl: 'http://example.com/profile-pictures/somebody.png',
phone: null,
name: null
}
Now we get this JSON in our android app, and turn it into JSONObject model:
JSONObject profileInfo = new JSONObject(profileInfoJson);
And we bind UI views to data:
email.setText(profileInfo.getString("email"));
phone.setText(profileInfo.getString("phone"));
name.setText(profileInfo.getString("name"));
Then in our TextView or EditView we have null string, instead of having nothing.
It's possible that we check null values using if-then statements, but that's too much for a real-world application with so many fields.
Is there a way to configure JSONObject to gracefully handle null strings?
Update: I used optString with a fallback, as suggested, but it has no effect:
firstName.setText(profileInfo.optString("firstName", ""));
And the result is the same EditText has null in it.

Use optString, if no suitable value is found then the second parameter will be returned instead of exception or null
phone.setText(profileInfo.optString("phone","nophone"));
name.setText(profileInfo.optString("name","noname"));
Returns the value mapped by name if it exists, coercing(try to cast) it if
necessary, or fallback(return second parameter)if no such mapping exists.

Related

ObjectUtils defaultIfNull returning default value even when first param is not null

target = ObjectUtils.defaultIfNull(getCustomerByAddressID(sourceData, supportParam),
createNewCustomer(supportParam));
I am passing 2 functions to ObjectUtils.defaultIfNull. First is function which returns Customer if found by AddressID else null, second param is function which create new Customer.
After execution I am seeing 2 Customers, debug is showing even after getCustomerByAddressID(sourceData, supportParam) returns not null value - createNewCustomer(supportParam) is getting executed.
Is this issue because of Code formatting or what am I missing? Should I use Optional.ofNullable().orElse() instead of ObjectUtils.defaultIfNull?
You can use Optional.ofNullable(getCustomerByAddressID(sourceData, supportParam)).orElseGet(() -> createNewCustomer(supportParam)) if you don't want createNewCustomer(supportParam) to be invoked, even if getCustomerByAddressID(sourceData, supportParam) is null
You should use
target = Optional.ofNullable(getCustomerByAddressID(sourceData, supportParam))
.orElseGet(() -> createNewCustomer(supportParam));
Because both Optional.ofNullable().orElse() and ObjectUtils.defaultIfNull() which uses ternary operator internally, always call "orElse" part.

How to remove the json key value with JSonPath in java

I know it could be a duplicate, but still posting my question as i could not find the exact answer what i am looking for. I am having an json object (or string) like below.
String str = "{
"status" : {
"timestamp" : "2020-04-30T01:00:00 000Z"
"error" : 0,
"error_message" : null,
"execution" : "completed"
}
}
";
I will get the a same kind of response from my REST API testing, but after each call the 'timestamp' key will be having a dynamic date and time value with the time respect to the call made. And here i compare my expect json with the actual json as a whole sting comparison using JSONAssert. As the timestamp value is different it always fails for me.
So my question is before i do any comparison, i would like to remove the 'timestamp' key and its value from json to compare and so it will pass my case. I tried by using JsonPath also, but did not works. Any help on this please?
JSONAssert allow you to make a customized comparator while doing asserts [1].
In your case it's easy as:
JSONAssert.assertEquals(expectedJson,
actualJson,
new CustomComparator(JSONCompareMode.LENIENT,
skips("status.timestamp","another.json.path", ...)));
private static Customization[] skips(String... jsonPaths) {
return Arrays.stream(jsonPaths)
.map(jsonPath -> Customization.customization(jsonPath, (o1, o2) -> true))
.toArray(Customization[]::new);
}
Here we are defining CustomComparator, with customization which takes JSONPath (status.timestamp) and takes a ValueMatcher (lambda) which compares two values for that specific JSONPath.
In our case we will always return true, which will effectively skips the value (no matter with what we are comparing that value it's always true).
Edit: As you can see CustomComparator's constructor takes varargs of Customizations, hence you can provide more than one field to be ignore from comparison.
[1] http://jsonassert.skyscreamer.org/apidocs/org/skyscreamer/jsonassert/Customization.html
[2] http://jsonassert.skyscreamer.org/apidocs/org/skyscreamer/jsonassert/comparator/CustomComparator.html

GMail API aliases.getSendAs().get(0).getDisplayName returns empty (only for 0, not for the next ones)

I'm using Java GMail API and everything is working good for sending e-mails, collecting data from my profile, etc.
The only problem is that, while I can get the Signature for my 0-th element of the list of SendAs aliases, I can't get the Display Name: it returns an empty String. Both work for the other aliases (get(1) and subsequent numbers). It seems that the problem is on 0, tried on different authenticated users with Name set and it remains the same.
ListSendAsResponse aliases = service.users().settings().sendAs().list("me").execute();
SendAs mimmo = aliases.getSendAs().get(0);
actualsign = mimmo.getSignature();
sendername = mimmo.getDisplayName();
In Gmail API, there are two different ways to retrieve alias(es):
getSendAs() and SendAs.Get(java.lang.String userId,java.lang.String sendAsEmail)
The first one returns you a list of all alias, the second returns you one alias ressource - the one with the specified userId and sendAsEmail parameters.
If what you want to do it to retrieve the first element of the getSendAs() response, you should do it with getSendAs()[0] and not with the Java method get.
Sample:
SendAs mimmo = aliases.getSendAs()[0];
System.out.println(mimmo.getDisplayName());
It is always useful to test with the Try this API what response a method returns. Thereby, the userId can be set to me.

How to send null params in HTTP POST/GET [duplicate]

This question already has answers here:
How to send NULL in HTTP query string?
(4 answers)
Closed 4 years ago.
What is the correct approach for sending null values in http post/get requests.
Important: I'm meaning to send NULL value for an specific param of the request, not empty strings or missing fields.
I considered the following options as incorrect
http://test.com?param1=&param2=bar //this is an empty string, not a null
http://test.com?param1=null&param2=bar //this is a string with content "null"
http://test.com?param2=bar //param 1 is missing, not null
Does sending a NULL make sense in HTTP (and is standardised) or should I fallback to any of the previous options? In the latter case, whats is the standard approach
In java when I use:
URLEncoder.encode(null, "UTF-8")
It crashes with Attempt to invoke virtual method 'int java.lang.String.length()' on a null object reference
I also get a crash when using OkHttp3 lib and try to add a null param
FormBody.Builder bodyBuilder = new FormBody.Builder();
bodyBuilder.add("param1",null)
The only sensible option you did not disqualify in the question is parameter without value.
http://example.com?param1&param2&param3=foo
Though I would go for not adding the property at all to the query string, as it results in the value to be "not having a value" hich is what null usually means.
Just use a blank "" string instead of null param if you are add null param here it will show exception because it find a null value to send http request

Data types on GAE server and Android client

I'm using java GAE server. I store List on my entity (as strings are very limited in length in GAE). I send Map to the client through the Endpoint, and I put this list under some key. Then I retrieve this list on Android client - and I get classcast exception. It appears that HashMap< String, Object > sent from GAE server is seen as JsonMap on Client. Whatever. I proceed, I retrieve my List... and how surprised I was to find out that on the client I got List< ArrayMap >, and on this ArrayMap, my Text is under the key named "value".
There is even more. Under one of the keys in the JsonMap, I had a null value. I retrieve it... and it appears as Object (which is not null). Calling toString on this object gives me some crappy string...
Could anyone tell me why these things are happening? Sure, I can just accept how it, but its strange and not logical, and undocumented... Why my List< Text > magically converts into List< ArrayMap >? How likely is that it varies with, lets say, Android version, or, I don't know, with weather outdoor?... Anyone could help me understand these situations? Or point me some relevant documentation / articles?
Example server-side:
#ApiMethod(name = "retrievePlayer")
public Map<String, Object> retrievePlayer(Map<String, Object> data, User user) throws Exception, OAuthRequestException, IOException {
Map<String, Object> result = new HashMap<String, Object>();
List<Text> list = new ArrayList<Text>();
list.add(new Text("something"));
result.put("myList", list);
result.put("myNull", null);
return result;
}
On the client side, the "result" is of type JsonMap. The "myList" is of type ArrayList (ok). myList.get(0) is of type ArrayMap, and its one-element ArrayMap - the element inside this map has key named "value", and a value of "something". The "myNull" is of type Object and is not null, its toString() method shows something like [Ljava.lang.Object;#1db9742.
I resolved the issues by returning empty string instead of null. For the List< Text >, I iterate through it on and add all the Texts as Strings to new List< String >, and then return this new list (but it costs cpu usage on the server)... I thought it will work more predictably and out-of-the-box.
In this particular example, the method will return an instance of Map<< String, Object>>. If you try to call this endpoint using https://your_app_id.appspot.com/_ah/api/explorer, the json return will be
{ "myList" : { { "value": "something"} }, "myNull" : null }.
The returned json seems to be correct and a null is returned for "myNull". The reason you are getting an instance of Object and not null is because of the handling of JSON null by the JSON library. Please check this link for more explanation (JSON null section).
As for why the List<< Text>> magically converts into List<< ArrayMap>>, this is because you define the return as an instance of Map<< String, Object>> and the returned json does not contain any type information. I think when converting to client objects the type information is obtained from the generated client code which is based on the signature of the ApiMethod.

Categories