I am parsing input JSON. For a field, there are 3 possibilities:
the field is absent;
the value is set to null;
the value is set to something valid.
Different behavior is implemented: for an absent value in the JSON, the default value is inserted into the database; for a null value in the JSON, a null value is inserted into the database.
I thought about Optional to model this:
public class Data {
private Optional<String> field;
}
Which of the following two options make most sense?
If field is null, the field was absent in the JSON. If field is Optional.empty, the field is null in the JSON.
If field is null, the field was null in the JSON. If field is Optional.empty, the field is absent in the JSON.
FWIW, I am using Jackson with module jackson-datatype-jdk8 to parse the input JSON.
I think you shouldn't use Optional for this scenario. As #dkatzel has mentioned in his answer, it's meant to be used as an API return value more than as a field.
Despite this academic discussion, you can accomplish what you want simply by initializing fields in your Data class to their default values:
public class Data {
private String field = DEFAULT_VALUE;
}
And then let Jackson do the rest.
EDIT as per OP's comment:
When your JSON comes with a null value for the field, Jackson will set it to null, and that's what will be stored in the database.
When your JSON does not contain the field, the DEFAULT_VALUE will be automatically loaded in your Data instance.
And when your JSON does actually contain a value for the field, Jackson will set it, and that value will reach the database.
EDIT 2, considering OP's requirement to find out if the field was either filled in, set to null or was absent in the input JSON, after parsing the JSON input:
If, after parsing the input JSON, you need to know whether the field was either filled in, set to null or was absent, then consider this example, which shows the approach I'd take:
public class Data {
private String field1 = "hello";
private Integer field2 = 10;
private Double field3 = 3.75;
private static final Data DEFAULTS = new Data(); // defaults will be kept here
public String getField1() {
return this.field1;
}
public void setField1(String field1) {
this.field1 = field1;
}
public Integer getField2() {
return this.field2;
}
public void setField2(Integer field2) {
this.field2 = field2;
}
public Double getField3() {
return this.field3;
}
public void setField3(Double field3) {
this.field3 = field3;
}
#Override
public String toString() {
return "Data [field1=" + this.field1 +
", field2=" + this.field2 +
", field3=" + this.field3 + "]";
}
public boolean isDefault(Function<Data, Object> getter) {
Object defaultProperty = getter.apply(DEFAULTS);
Object actualProperty = getter.apply(this);
return defaultProperty != null // needed to support fields with no default value
&& defaultProperty.equals(actualProperty);
}
public boolean isNull(Function<Data, Object> getter) {
return getter.apply(this) == null;
}
public boolean isSet(Function<Data, Object> getter) {
return !this.isNull(getter) && !this.isDefault(getter);
}
}
Here I've used a private static attribute to hold your Data's default values and 3 methods to query any field state (default, null or set). In order to determine which field to query, these methods receive a Function<Data, Object>, which are given a Data instance and return an Object that is supposed to be the desired field. (If you stop to think it, getters can be seen as functions that take the instance as input and return a specific field of the instance).
So later, when you need to know how a certain field arrived in your JSON input, just use those 3 query methods to find out:
ObjectMapper m = new ObjectMapper();
String json = "{\"field1\":null,\"field2\":20}";
Data data = m.readValue(json, Data.class);
System.out.println(data); // Data [field1=null, field2=20, field3=3.75]
System.out.println("field1 default ? " + data.isDefault(Data::getField1)); // false
System.out.println("field1 null ? " + data.isNull(Data::getField1)); // true
System.out.println("field1 set ? " + data.isSet(Data::getField1)); // false
System.out.println("field2 default ? " + data.isDefault(Data::getField2)); // false
System.out.println("field2 null ? " + data.isNull(Data::getField2)); // false
System.out.println("field2 set ? " + data.isSet(Data::getField2)); // true
System.out.println("field3 default ? " + data.isDefault(Data::getField3)); // true
System.out.println("field3 null ? " + data.isNull(Data::getField3)); // false
System.out.println("field3 set ? " + data.isSet(Data::getField3)); // false
I would say that the first option makes the most semantic sense. It also potentially allows for easier computation.
Where a field in java is null, it is implied that a value is missing, which matches the first option.
I suggest that you store these fields in a hash-map where the key is the JSON field name and the value is the JSON field's value. I also suggest you don't use an optional here (as it can add an unnecessary layer of complexity), and instead use either a null or non-null object in the hashmap.
HashMap<String, Value> jsonFields = new HashMap<String, Value>();
boolean hasField1 = false;
Value field1Value = null;
if(jsonFields.contains("field1"){ // It is present in the JSON file
field1Value = jsonFields.get("field1"); // "null" here would mean that the JSON field was set to "null"
hasField1 = true;
}
The second choice makes more sense to me. null means null and empty means not present.
However, Optional shouldn't really be used as a field. It's supposed to be used as an API return value.
Could you instead store the data in a Map that allows null values? And if the key (your field) isn't present in the map, then return Optional.empty ?
Neither? I would annotate my POJO fields with #DefaultValue(). Then your possibilities are a null value or a non-null value specified in JSON, or the default if the field was omitted from JSON. And you can then just persist the POJO without any special per-field analysis.
If you are dealing with Object instead of String, here's a solution I find elegant:
use Optional.empty(); if there is no value
use Optional.of(value) if there is a value
use Optional.of(specialValue) if the value is null
where specialValue is a static singleton you can easily test, for instance: ObjectUtils.NULL (from commons.lang).
Then you can easily test your optional:
if (optional.isPresent()) {
if (ObjectUtils.NULL.equals(optional.get())) {
// value is there and null
} else {
// value is there and not null
}
} else {
// value is not there
}
Related
Say i have the below JPA method :
public List<FrequencyCode> findAllByNameContainingAndAllowExplicitDosingTimesEqualsOrderByName(String name, Boolean allowExplicitDosingTimes);
This method is called by a user filtering a list of these objects with an input field and a select field :
The boolean value can be true, false or null in this case if the user is not making a search with that field. It looks like JPA is ACTUALLY searching for a null value when i would like it to ignore any null values. I have been able to make this combined search work with the below code :
#Override
public List<FrequencyCode> findAllWithFilters(String name, Boolean allowExplicitDosingTimes)
{
if (allowExplicitDosingTimes == null)
{
return ((FrequencyCodeRepository) baseRepository).findAllByNameContainingOrderByName(name);
}
else if (allowExplicitDosingTimes == true)
{
return ((FrequencyCodeRepository) baseRepository).findAllByNameContainingAndAllowExplicitDosingTimesTrueOrderByName(name);
}
else if (allowExplicitDosingTimes == false)
{
return ((FrequencyCodeRepository) baseRepository).findAllByNameContainingAndAllowExplicitDosingTimesFalseOrderByName(name);
}
return null;
}
This works but, obviously, on a page with 8 search options this would become a nightmare. The String parameters do not have this problem because they are actually an empty String when the user doesn't choose a filter. This paired with the Containing keyword, any value contains "" so it behaves as if that parameter is ignored which is exactly what I want for other types. Is there a way for a JPA findAll...() method to simply ignore null parameters?
******SOLUTION******
Here is how i made this work with the help of the accepted answer :
FrequencyCode fc = new FrequencyCode();
fc.setName(name);
fc.setAllowExplicitDosingTimes(allowExplicitDosingTimes);
ExampleMatcher matcher = ExampleMatcher.matching()
.withMatcher("name", match -> match.contains())
.withMatcher("allowExplicitDosingTimes", match -> match.exact())
.withIgnorePaths("id", "uuid")
.withIgnoreNullValues();
Example<FrequencyCode> example = Example.of(fc, matcher);
List<FrequencyCode> frequencyCodes = ((FrequencyCodeRepository) baseRepository).findAll(example);
You HAVE to tell it to ignore any ID fields or really any other fields you do not intend to search with but this is INCREDIBLY powerful!
Thanks!
You can use Example like this
#Override
public List<FrequencyCode> findAllWithFilters(String name, Boolean allowExplicitDosingTimes) {
FrequencyCode fc = new FrequencyCode();
//I assume that you have setters like bellow
fc.setName(name);
fc.setAllowExplicitDosingTimes(allowExplicitDosingTimes);
ExampleMatcher matcher = ExampleMatcher.matching().withIgnoreNullValues();
Example<FrequencyCode> example = Example.of(fc, matcher);
return ((FrequencyCodeRepository) baseRepository).findAll(example);
}
I have a VO similar to this.
public class Job
{
private Long id;
private String createdBy;
private Set<JobStatus> jobStatuses;
}
And many more similar fields. I want to iterate through the fields in VO and set 'NA' for all String fields which doesn't have data. This is what I have so far.
Job job = getJob();//getting the Job populated
BeanInfo beanInfo = Introspector.getBeanInfo(Job.class);
for (PropertyDescriptor propertyDesc : beanInfo.getPropertyDescriptors()) {
if (propertyDesc.getReadMethod().invoke(job) == null
&& propertyDesc.getPropertyType() == String.class) {
propertyDesc.getWriteMethod().invoke(job, "NA");
}}
This works well. But now I need to iterate through the other fields which are objects by itself and do the same dynamically. Like Set<JobStatus> jobStatuses. How can I do it ?
Frankly, this is just bad practice.
If you want to save your object in the database and want the empty attributes to be saved as 'NA' (for whatever reason) just set the default value for the column to 'NA'.
You can also initialize the object with attribute values = 'NA' in the constructor saves you much time that looping through the object properties.
If you don't initialize those variables in the constructor with N/A, you could also have a method on each object that sets the null variables to N/A and just call it.
You cannot assign string values to the Java objects that are not String types. But I am assuming it is ok for you assign an empty object constructed from a default constructor, if exists, to properties that are null. With that assumption, try the following solution:
for (PropertyDescriptor propertyDesc : beanInfo.getPropertyDescriptors()) {
if (propertyDesc.getReadMethod().invoke(job) == null
&& propertyDesc.getPropertyType() == String.class) {
propertyDesc.getWriteMethod().invoke(job, "NA");
}
else if (propertyDesc.getReadMethod().invoke(job) == null
&& propertyDesc.getPropertyType() != String.class) { //Other than String types
propertyDesc.getWriteMethod().invoke(job, propertyDesc.getPropertyType().newInstance());
}
}
Don't forget to handle this code with a try-catch block. Not all objects in your class might have a default constructor in which case you might want to further customize your code.
I know I could use the putIfAbsent of ConcurrentHashMap. However, I need to do a webservice call to fetch a value for the given key if it does not exist and then store it (kind of caching) so I don't need to the next time same key is being used. Which of the following is correct? I think second version is necessary with the synchronized.
Update 1: I cannot use any functional interface.
Update 2: Updating the code snippet based on the reply from Costi Ciudatu
private static final Map<String, String> KEY_VALUE_MAP = new ConcurrentHashMap<>();
public String getValueVersion1(String key) {
String value = KEY_VALUE_MAP.get(key);
if (value != null) {
return value;
}
// Else fetch the value for the key from the webservice.
value = doRestCall(key);
KEY_VALUE_MAP.putIfAbsent(key, value);
return value;
} // Version 1 Ends here.
public synchronized String getValueVersion2(String key) {
String value = KEY_VALUE_MAP.get(key);
if (value != null) {
return value;
}
// Else fetch the value for the key from the webservice.
value = doRestCall(key);
KEY_VALUE_MAP.put(key, value);
return value;
} // Version 2 ends here.
You should have a look at ConcurrentMap#computeIfAbsent which does that for you atomically:
return KEY_VALUE_MAP.computeIfAbsent(key, this::doRestCall);
Edit (to address your "no functional interface" constraints):
You only need "client side locking" if you want to make sure that you only invoke doRestCall once for any given key. Otherwise, this code would work just fine:
final String value = KEY_VALUE_MAP.get(key);
if (value == null) {
// multiple threads may call this in parallel
final String candidate = doRestCall(key);
// but only the first result will end up in the map
final String winner = KEY_VALUE_MAP.putIfAbsent(key, candidate);
// local computation result gets lost if another thread made it there first
// otherwise, our "candidate" is the "winner"
return winner != null ? winner : candidate;
}
return value;
However, if you do want to enforce that doRestCall is invoked only once for any given key (my guess is you don't really need this), you will need some sort of synchronization. But try to be a bit more creative than the "all-or-nothing" approach in your examples:
final String value = KEY_VALUE_MAP.get(key);
if (value != null) {
return value;
}
synchronized(KEY_VALUE_MAP) {
final String existing = KEY_VALUE_MAP.get(key);
if (existing != null) { // double-check
return existing;
}
final String result = doRestCall(key);
KEY_VALUE_MAP.put(key, result); // no need for putIfAbsent
return result;
}
If you want to use this second (paranoid) approach, you can also consider using the key itself for locking (to narrow the scope to the minimum). But this would probably require you to manage your own pool of keys, as syncrhonized (key.intern()) is not good practice.
This all relies on the fact that your doRestCall() method never returns null. Otherwise, you'll have to wrap the map values within an Optional (or some home-made pre-java8 alternative).
As a (final) side note, in your code samples you inverted the use of put() and putIfAbsent() (the latter is the one to use with no external synchronization) and you read the value twice for null-checking.
preparedStatement1.setInt(1,jsonObject1.getInteger("userid"));
database is mySQL. My userID field in the database is int type and is an optional field.
How can I easily pass NULL into this from json?
{"userid":null} is getting me error.
The way you set an int field to null is with setNull rather than setInt. setInt takes an int argument, so it cannot receive null as a value.
In your case you can use this to cover the case where the field may or may not be null:
Integer userId = jsonObject1.getInteger("userid");
if (userId==null) {
preparedStatement1.setNull(1, Types.INTEGER);
} else {
preparedStatement1.setInt(1, userId);
}
If you wanted you could write a helper method to do this more conveniently.
public static void setInteger(PreparedStatement stmt, int index, Integer value) {
if (value==null) {
stmt.setNull(index, Types.INTEGER);
} else {
stmt.setInt(index, value);
}
}
then in context your operation is back to one line:
setInteger(preparedStatement1, 1, jsonObject1.getInteger("userid"));
I am receiving list of fields. Near About to 60 fields.
From that I have to check 50 fields that are they null or empty, if not then I ll have to add them also in DB table.
Right now I am doing it manually using if condition. I am just thinking to do so, not implemented still yet.
Is there any better option then it ?
My Code :
if(ValidateData.checkIsNullOrEmpty(command.getSubscriptionStartYear())){
}
if(ValidateData.checkIsNullOrEmpty(command.getSubscriptionPeriod())){
}
if(ValidateData.checkIsNullOrEmpty(command.getExpectedArrivalTimeOfIssues())){
}
.....
.....
if(ValidateData.checkIsNullOrEmpty(command.getMaxNoOfClaims())){
}
Here command is class which receives Data from source.
Here ValidateData is a class :
It's method definition :
public static boolean checkIsNullOrEmpty(Integer arg){
if(arg != null) return true;
return false;
}
public static boolean checkIsNullOrEmpty(String arg){
if(!arg.trim().equals("") || !arg.trim().equals(" ") || arg.trim() != null) return true;
return false;
}
If anyone guide me or suggest me that is there any better option available ??
create a function like this:
public static bool AllNull(object... something)
{
for(var v :something)
if(v!=null){
if(v instanceof Integer)
// do integer validation
}else
//Err msg
}
Then you could call it like this:
if (AllNull(obj1, obj2, obj3, obj4, obj5, obj6))
{
// ...
}
if you want to be specific, separate strings and integers and make separate function like this one for each type you need
Edit
as i understod from your comment, u don't know varargs
varargs are useful for any method that needs to deal with an
indeterminate number of objects. One good example is String.format.
if you can edit command, you can mark each field that you want to check null with #NotNull, then use java reflect api to get all fields marked with #NotNull, and check whether some fields null or not
I think best solution for your problem is using Java Reflect.
Here is sample code to validate all field of an instance by Java Reflect.
Example I have one instance(pojo) of object PojoObj.
PojoObj pojo = new PojoObj("one1", 2, null, 4, "five", "Six");
Validate all fields by Java Reflect.
Class<PojoObj> aClass = PojoObj.class;
Field[] fields = aClass.getDeclaredFields();
for(Field field : fields) {
Object value = field.get(pojo);
Object type = field.getType();
if(value == null) {
System.out.println(field.getName() + " is null");
} else {
System.out.println(field.getName() + " is instanceof " + type + " and value = " + value);
}
}
Output:
fieldOne is instanceof class java.lang.String and value = one1
fieldTwo is instanceof long and value = 2
fieldThree is null
fieldFour is instanceof int and value = 4
fieldFive is instanceof class java.lang.String and value = five
fieldSix is instanceof class java.lang.String and value = Six