I use Jackson Json Mapper to parse queries on my server.
For example, I'm waiting for query which fits class My_class:
class My_class {
String a;
String b;
}
I deserialize queries this way:
public <T> T Deserialize(String json, Class<T> type) throws DeserializationException {
if (json == null || type == null) {
throw new IllegalArgumentException();
}
try {
return objectMapper.readValue(json, type);
} catch (JsonParseException e) {
throw new DeserializationException(e);
} catch (org.codehaus.jackson.map.JsonMappingException e) {
throw new DeserializationException(e);
} catch (IOException e) {
throw new DeserializationException(e);
}
}
Here are two example queries:
{"a":"test"}
{"a":"test", "b":null}
The problem is I want to know when a user sent me a query with only the field a and when he sent a query with field b set to null. The mapper marks field b as null in both situations.
What is the better way to do this (and to avoid writing my own deserializer)?
You can use the method isNull() to check if the JsonNode is a null literal.
boolean isNull()
Method that can be used to check if this node was created from JSON literal null value.
There is also isMissingNode(), which seems to have a similar purpose. It should return true if the node isn't actually present in the JSON document. This method is useful e.g. if you want to use default values for properties that aren't set explicitly.
boolean isMissingNode()
Method that returns true for "virtual" nodes which represent missing entries constructed by path accessor methods when there is no actual node matching given criteria.
In my opinion you should avoid building something that depends on the difference between null and missing nodes though. You are essentially adding some kind of additional data type to the deserialized JSON: instead of just null most JSON APIs return in both cases, you now have null literal and missing node which is not a common practice. It is confusing and it may cause issues with your API because the client needs to do extra work to configure its serializer to distinguish those two states too. It's possible that not all JSON libraries support that.
Related
"app":{
"icon":{
"icon":"TOP_RATED"
},
"message":{
"_type":"TextSpan",
"text":"Top Rated"
}
}
I keep seeing the following code in one of the projects that I have inherited. The JSON response above is parsed as follows
// itemObject has the entire json response
// appObject is a POJO with icon, type fields
String icon= JsonPath.with(itemObject).getAsString("icon/icon");
appObject.setIcon(icon);
String type = "";
try {
type = JsonPath.with(itemObject).getAsString("message/_type");
catch(IllegalArgumentException e) {
// do nothing if type is not found in response
} finally {
// set type to empty string if it's not found
appObject.setType(type);
}
In the scenario, when _type doesn't exist for a specific app, would it be best to surround it with a try/catch block as shown above? It just seems wrong to use try/catch/finally block to process business logic instead of error handling. What is a better way to do the same and can Java 8 Optional help with this?
I find the org.json package simple and straightforward. It is found here. The org.json.JSONObject class, for example, contains the public boolean has(String key) method, which is used to check if a certain key exists.
Returns true if this object has a mapping for name. The mapping may be NULL.
You can check this way where 'HAS' - Returns true if this object has a mapping for name. The mapping may be NULL.
if (json.has("status")) {
String status = json.getString("status"));
}
if (json.has("club")) {
String club = json.getString("club"));
}
You can also check using 'isNull' - Returns true if this object has no
mapping for name or if it has a mapping whose value is NULL.
if (!json.isNull("club"))
String club = json.getString("club"));
http://developer.android.com/reference/org/json/JSONObject.html#has(java.lang.String)
I have a simple enum I'd like to serialize and deserialize. The class looks like this:
public enum TipusViatge {
OCI,
NEGOCIS,
FAMILIA;
#Override
public String toString() {
return name().toUpperCase();
}
}
The thing is, I send it via a restful call and the receiving side may receive any type (so it only knows it will receive Object). So Jackson should be able to figure out the type of the argument to deserialize it.
Is it possible to do so? I was thinking that including the class name in the resulting json should allow Jackson to figure out the type, but I've been unable to do so.
I have worked over this problem for a while.
1st you could deserialize your json with Map<String, Object>. It alway works; you get standard types (your enumeration will be readed as plain string).
2nd in general case you alway know what kind of object you read. This is top-level object and you can set it to Jackson mapper: mapper.readerFor(cls).readValue(json). In case of your enumeration is a part of this cls object, then Jackson knows the type and just read value and parse to it.
3rd you actually could have multiple objects for one json string. I am talking about inheritance. And you could look at #JsonTypeInfo in Jackson documentation.
4th imagin that you read a json source and do not know what you read. In this case, you could ask Jackson to write marker at the beginning of the object. Just like you asking about class name. I think it relates to #JsonRootName. You can look on it here: Jackson JSON Deserialization with Root Element
I think that it is clean now how to work with objects in Jackson. I mean that we know how to tell Jackson what element we want to deserialize. Now we have one problem: how to serialize json -> our enumeration.
5th this is not a problem and works out of the box. Jackson uses name() method to serialize enumeration, and valueOf() to deserialize. You can look at it closer in EnumDeserializer in Jackson.
6th I do not like this behaviour, becuase it is case-sencitive. I faced with situation that when people write json string manually, the use lower-case and cannot deserialize it. Moreover, I belive, that writing enumeration constants directly to the json file is a bad practise, because If I want to refactor names of the enumeration, all existed json string should be modified as well (brrr). To solve thiese issues, I do following trick:
1. Implement EnumId interface with default implementation of parseId(String id) with using getId() to identify enumeration constants and using ignore case for compare.
1. I add id field to the enumeration
2. Add getId() - for serialization
3. Add parseId(String id) - for deserialization
4. Add new module in Jackson ObjectMapper with my customer serializer (it
should use `getId()` instead of `name()`).
if (enumId != null) {
generator.writeString(enumId.getId());
}
And tell Jackson how to deserialize this enum. Here this is dificult situation, becuase in different sources, Jackson use different deseriaization hierarchy and just adding another module to ObjectMapper with custom deserialize (just like in 4.) will not be working with all situations. To solve this problem, I found out that we could add #JsonCreator to parseId(String id) method in enumeration and Jackson will be using it in all situation.
I think that is all about this topic. I give you a code example to make it more clearly (it is better to write once, then explain twice):
public interface EnumId {
String name();
default String getId() {
return name().toLowerCase();
}
static <T extends Enum<?> & EnumId> T parseId(Class<T> cls, String id) {
T res = parseId(cls.getEnumConstants(), id, null);
if (res != null) {
return res;
}
throw new EnumConstantNotPresentException(cls, id);
}
static <T extends EnumId> T parseId(T[] values, String id, T def) {
for (T value : values) {
if (id != null ? id.equalsIgnoreCase(value.getId()) : value.getId() == null) {
return value;
}
}
return def;
}
static <T extends EnumId> T get(T value, T def) {
return value != null ? value : def;
}
}
public enum TipusViatge implements EnumId {
OCI,
NEGOCIS,
FAMILIA;
#JsonCreator
public static TipusViatge parseId(String id) {
return EnumId.parseId(TipusViatge.class, id);
}
}
The docs say the JsonObject#get method returns null if no such member exists. That's not accurate; sometimes a JsonNull object is returned instead of null.
What is the idiom for checking whether a particular field exists in GSON? I wish to avoid this clunky style:
jsonElement = jsonObject.get("optional_field");
if (jsonElement != null && !jsonElement.isJsonNull()) {
s = jsonElement .getAsString();
}
Why did GSON use JsonNull instead of null?
There is an answer for what are the differences between null and JsonNull. In my question above, I'm looking for the reasons why.
Gson, presumably, wanted to model the difference between the absence of a value and the presence of the JSON value null in the JSON. For example, there's a difference between these two JSON snippets
{}
{"key":null}
your application might consider them the same, but the JSON format doesn't.
Calling
JsonObject jsonObject = new JsonObject(); // {}
jsonObject.get("key");
returns the Java value null because no member exists with that name.
Calling
JsonObject jsonObject = new JsonObject();
jsonObject.add("key", JsonNull.INSTANCE /* or even null */); // {"key":null}
jsonObject.get("key");
returns an instance of type JsonNull (the singleton referenced by JsonNull.INSTANCE) because a member does exist with that name and its value is JSON null, represented by the JsonNull value.
I know question is not asking for a solution, but I came here looking for one. So I will post it in case someone else needs it.
Below Kotlin extension code saves the trouble of checking for null and isJsonNull separately for each element
import com.google.gson.JsonElement
import com.google.gson.JsonObject
fun JsonObject.getNullable(key: String): JsonElement? {
val value: JsonElement = this.get(key) ?: return null
if (value.isJsonNull) {
return null
}
return value
}
and instead of calling like this
jsonObject.get("name")
you call like this
jsonObject.getNullable("name")
Works particularly great in nested structures. Your code eventually would look like this
val name = jsonObject.getNullable("owner")?.asJsonObject?.
getNullable("personDetails")?.asJsonObject?.
getNullable("name")
?: ""
I have the following variable annotated for data validation:
#Size(min=8, max=16, message="the size of the parameter must be between 8 and 16")
private String param;
However, the param can be null. It is required that it be 8-16 chars long only if it is not null. The problem I face is if the client app (JSON API) supplies an empty string, I want to treat it as though it were not supplied at all, i.e. is null. I was wondering if there is an elegant way to do this using the javax.validation annotations, i.e. convert an empty string to null, as opposed to the plain Java way the way I'm doing it right now:
public void setParameter(String _param) {
if(_param != null && !_param.trim().isEmpty()){
this.param = _param;
} else {
this.param = null;
}
}
I would like to have a very simple setter:
public void setParameter(String _param) {
this.param = _param;
}
and have the is-empty-string boilerplate done by an annotation. Is there a way to do it?
You could can implement your own custom constraint validator.
see here. I've used this many times and works like a charm.
https://docs.jboss.org/hibernate/validator/5.0/reference/en-US/html/validator-customconstraints.html
You would just need to set this condition (if null return "" or vice-versa) in the isValid method.
I using in my project GSON library, everything is fine, but now i'm stuck with a problem, where i need to use a custom deserializer on unquoted values.
I have the following value, and need to parse from json:
[ ["county","=", field_name], ["name", "ilike", "username"] ]
I need to parse unquoted values with a custom deserializer, to a wrapper class like:
public class StringField {
private String value;
public String getValue() {
return value;
}
}
And value will have "field_name" as string.
The problem is that the data is not valid JSON.
JSON does not permit such "unquoted value" strings such as field_name and neither does Gson. Either fix the input such that it is valid JSON (perhaps "$field_name$") - or use a tool (i.e. not Gson) that can cope with non-JSON text that resembles JSON.
This situation can't be corrected with Custom Deserialization because the data isn't even parsed correctly to Json tokens: Gson will throw an exception as the invalid/non-JSON is encountered.
At the very least this would require creating a customized JsonReader implementation that can read "barewords" as strings. However, this is problematic to do because JsonReader does not conform to any specialized interfaces (so it must be subclassed, oops!) and is final (so it can't be subclassed, oops!). As such, unless willing to edit the Gson library source: not possible.
With the below code, I parsed your JSON without problems, I left Gson decide how to parse it, except assuming it contained a List outermost. And the result was a List of Lists of Strings. I did not understand very well why you need StringField class.
package stackoverflow.questions;
import java.util.List;
import com.google.gson.*;
public class Q20557131 {
public static void main(String[] args){
String json = "[[\"county\",\"=\", field_name], [\"name\", \"ilike\", \"username\"]]";
Gson g = new Gson();
List outerList = g.fromJson(json, List.class);
List innerList = (List) outerList.get(0);
for(Object o: innerList)
System.out.println(o.getClass());
}
}
By default, Gson 2.2.4 is lenient, even if has the lenient property set to false, from documentation
Configure this parser to be be liberal in what it accepts. By default, this parser is strict and only accepts JSON as specified by RFC 4627. Setting the parser to lenient causes it to ignore the following syntax errors:
....
Strings that are unquoted or 'single quoted'.
...
even if documentation states that property is false by default, in the source code of the JsonReader#fromJson:
public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
boolean isEmpty = true;
boolean oldLenient = reader.isLenient();
reader.setLenient(true); <-- always true
try {
reader.peek();
isEmpty = false;
TypeToken<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT);
TypeAdapter<T> typeAdapter = getAdapter(typeToken);
T object = typeAdapter.read(reader);
return object;
} catch (EOFException e) {
...
I've solved this problem years ago in another way (sorry for delayed). Wrote symbolic preprocessor class, which replace by regexp labels like field_name with actual values from model and then parsed json.