I know this kind of question was raised in the past but not exactly the same issue so i found the right to ask this question.
I'm using JERSEY together with JACKSON for REST web service (JAVA 1.8_011 + Tomcat v7.0 + windows 7 + JERSEY-common 2.23.2 + JACKSON 2.8.2)
One of my POJO field has the following setter:
public void setEndDate(LocalDateTime endDate) {
if (this.startDate != null && this.startDate.isAfter(endDate))
{
throw new IllegalArgumentException("Start date must to be before End date");
}
this.endDate = endDate;
}
my web service is the following:
#PUT
#Path("/updateCoupon")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.TEXT_PLAIN)
public String updateCoupon(Coupon coupon) {
try
{
//Coupon tmpCoupon = new Coupon(coupon);
System.out.println("*" + coupon.getEndDate().toString() + "*");
getFacade().updateCoupon(coupon);
return "ok";
}
catch (FacadeException | IllegalArgumentException e)
{
return e.getMessage();
}
}
JSON:
{
"startDate":"2016-11-04T00:00",
"endDate":"2016-11-09T00:00",
"amount":7,
"id":143,
"image":"390_290_5cc10a4d-9a3f-4cfc-8.jpg",
"message":"gfd",
"price":3.34,
"title":"n37",
"type":"HEALTH"
}
After debugging and tests the problem is that the JSON does not use my setter to transform from JSON to the POJO (it happens in more setters so the setter it self is not the issue)
Thanks
Your current code for Coupon is dependent on the order that the setters are invoked. If setEndDate is invoked before setStartDate, the validation in setEndDate can't actually use the startDate field.
To fix the problem, you could:
remove setters from your bean and convert to initializing with a constructor that performs validation logic
use a static factory method and label it with #JsonCreator, so that Jackson will use that instead of the constructor
some combination of the two things above
switch to some kind of bean object creator which lets you author a check method to be run after all setters have been invoked (essentially an automatic version of the second option), such as Immutables, or FreeBuilder
Related
I've created the annotation I want to put on some fields of a class.
I want the annotation to check one of two or more fields:
#Documented
#Target({ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
public #interface OneOfFields {
}
For example - the class:
public class MyClassRq {
#OneOfFields
private String stringOne;
#OneOfFields
private String stringTwo;
}
If I create an HttpRequest with the body and set both fields, I want to get an exception, javax.validation exception is also possible.
What is the best way to write the validator?
Annotations can be processed in two phases:
At compile time (in this case through an Annotation Processor)
At runtime (in this case through reflection)
It depends on when you want to perform the check. Considering that it seems you want to check this at runtime (i.e. when you receive the object), then you could create a sample method that takes an object, scans all the fields of the object for the annotation #OneOfFields and if more than one is not null, then it throws an exception:
public static <T> T validate(T input) {
try {
int numberOfAnnotatedNonNull = 0;
for (Field field : input.getClass().getDeclaredFields()) {
if (field.isAnnotationPresent(OneOfFields.class) && (field.get(input) != null)) {
numberOfAnnotatedNonNull++;
if (numberOfAnnotatedNonNull > 1) {
throw new IllegalStateException("More than one field annotated by #OneOfFields has been set for this object");
}
}
}
return input;
} catch (IllegalAccessException e) {
throw new IllegalStateException("Could not validate input object", e);
}
}
Sample usage:
MyClassRq myClassRq = validate(yourInput);
If the above yourInput of type MyClassRq is valid, then it will simply return the same object. Else, it will throw an exception.
Some notes:
Here I'm throwing as soon as I find more than one field which is non null. You may of course create a cleaner error message (for example by collecting all the fields which are illegally set and returning their names)
Here I'm throwing a standard IllegalStateException but you can (you should probably) create your own custom exception
Don't forget to check that T input is not null (if it is, my code will crash).
This is a sample usage of the standard Java Reflect API, there are several ways of reaching the same purpose (I've just shown you the most "readable")
I have a REST service that uses a POJO. Here is the method:
#POST
#Path("terminate")
#Produces({"application/xml", "application/json"})
#Consumes({"application/xml", "application/json"})
public TerminateActorCommand terminateActor(TerminateActorCommand cmd) {
System.out.println("Running terminate: " + cmd);
Query query = em.createNamedQuery("Actor.terminate");
query.setParameter("eid", cmd.getActorEid());
query.executeUpdate();
return cmd;
}
Here is the POJO
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Date;
import javax.xml.bind.annotation.XmlRootElement;
/**
*
* #author mike
*/
#XmlRootElement
public class TerminateActorCommand {
String actorEid;
String terminatorEid;
String reason;
Date effectiveTerminationDate;
public TerminateActorCommand() {
}
#JsonCreator
public TerminateActorCommand(#JsonProperty("actorEid") String actorEid, #JsonProperty("terminatorEid") String terminatorEid,
#JsonProperty("reason") String reason) { //, #JsonProperty("effectiveTerminationDate") Date effectiveTerminationDate) {
this.actorEid = actorEid;
this.terminatorEid = terminatorEid;
this.reason = reason;
//this.effectiveTerminationDate = effectiveTerminationDate;
}
public CommandType getCommandType() {
return CommandType.TERMINATE_ACTOR;
}
public String getActorEid() {
return actorEid;
}
public String getTerminatorEid() {
return terminatorEid;
}
public String getReason() {
return reason;
}
public Date getEffectiveTerminationDate() {
return effectiveTerminationDate;
}
#Override
public String toString() {
return "TerminateActorCommand{" + "actorEid=" + actorEid + ", terminatorEid=" + terminatorEid + ", reason=" + reason + ", effectiveTerminationDate=" + effectiveTerminationDate + '}';
}
}
When I call this with CURL:
curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X POST -d '{"actorEid":"mb995a", "terminatorEid":"mb995a","reason":"testing"}' http://127.0.0.1:8080/actor-service/webresources/net.mikeski.auth.entities.actors/terminate
I get the return value and see the print statement,but the TerminationCommand's fields are all null. I get an instantiated object but the JSON I'm sending does not get populated on the object.
Why???
Here's the output:
Info: Running terminate: TerminateActorCommand{actorEid=null, terminatorEid=null, reason=null, effectiveTerminationDate=null}
I've walked through all of the code included in your question and I have some suggestions:
Within the TerminateActorCommand POJO, add the #JsonProperty annotation to the members that match your JSON properties (the presence of the property accessor methods but absence of mutator methods may be confusing Jackson):
#JsonProperty String actorEid;
#JsonProperty String terminatorEid;
#JsonProperty String reason;
If adding the #JsonProperty doesn't resolve your issue, examine the no-arg constructor that is currently defined within your TerminateActorCommand class. When you use the Jackson #JsonCreator annotation, there is no need to define a no-arg constructor, but if Jackson is unable to find a good match during deserialization, it will fallback to using a no-arg constructor. My guess is that the no-arg constructor is what is currently being called during JSON deserialization (thus the null property values), so I would suggest either removing that constructor or, if it is needed (maybe in other parts of your code), add a System.out.println within the no-arg constructor, so you will know for certain if that constructor is being called when Jackson performs the JSON deserialization.
There is also an unnecessary space between the first and second properties in your cURL command -d payload specification and that shouldn't cause a problem, but removing that space will rule that out as a possible problem.
I think the properties are not set because your fields/setters are not marked as #JsonProperty. Even though you have marked those as json properties in the parameterized constructor, marking these fields or setters with annotations should help because your library/framework might be using no-arg constructor to instantiate the object and then set the properties lazily on the created object.
My basic question: is there anything built that already does this automatically (doesn't have to be part of a popular library/package)? The main things I'm working with are Spring (MVC) and Jackson2.
I understand there are a few manual ways to do this:
Create a method in each class that serializes its specific properties into property=value& form (kind of stinks because it's a bunch of logic duplication, I feel).
Create a function that accepts an object, and uses reflection to dynamically read all the properties (I guess the getters), and build the string by getting each. I'm assuming this is how Jackson works for serialization/deserialization in general, but I really don't know.
Use some feature of Jackson to customly serialize the object. I've researched custom serializers, but it seems they are specific to a class (so I'd have to create one for each Class I'm trying to serialize), while I was hoping for a generic way. I'm just having trouble understanding how to apply one universally to objects. A few of the links:
http://techtraits.com/Programming/2011/11/20/using-custom-serializers-with-jackson/
http://wiki.fasterxml.com/JacksonHowToCustomSerializers
Use ObjectMapper.convertValue(object, HashMap.class);, iterate over the HashMap's key/value pairs, and build the string (which is what I'm using now, but I feel the conversions are excessive?).
I'm guessing there's others I'm not thinking of.
The main post I've looked into is Java: Getting the properties of a class to construct a string representation
My point is that I have several classes that I want to be able to serialize without having to specify something specific for each. That's why I'm thinking a function using reflection (#2 above) is the only way to handle this (if I have to do it manually).
If it helps, an example of what I mean is with, say, these two classes:
public class C1 {
private String C1prop1;
private String C1prop2;
private String C1prop3;
// Getters and setters for the 3 properties
}
public class C2 {
private String C2prop1;
private String C2prop2;
private String C2prop3;
// Getters and setters for the 3 properties
}
(no, the properties names and conventions are not what my actual app is using, it's just an example)
The results of serializing would be C1prop1=value&C1prop2=value&C1prop3=value and C2prop1=value&C2prop2=value&C2prop3=value, but there's only one place that defines how the serialization happens (already defined somewhere, or created manually by me).
So my idea is that I will have to end up using a form of the following (taken from the post I linked above):
public String toString() {
StringBuilder sb = new StringBuilder();
try {
Class c = Class.forName(this.getClass().getName());
Method m[] = c.getDeclaredMethods();
Object oo;
for (int i = 0; i < m.length; i++)
if (m[i].getName().startsWith("get")) {
oo = m[i].invoke(this, null);
sb.append(m[i].getName().substring(3) + ":"
+ String.valueOf(oo) + "\n");
}
} catch (Throwable e) {
System.err.println(e);
}
return sb.toString();
}
And modify it to accept an object, and change the format of the items appended to the StringBuilder. That works for me, I don't need help modifying this now.
So again, my main question is if there's something that already handles this (potentially simple) serialization instead of me having to (quickly) modify the function above, even if I have to specify how to deal with each property and value and how to combine each?
If it helps, the background of this is that I'm using a RestTemplate (Spring) to make a GET request to a different server, and I want to pass a specific object's properties/values in the URL. I understand I can use something like:
restTemplate.getForObject("URL?C1prop1={C1Prop1}&...", String.class, C1Object);
I believe the properties will be automatically mapped. But like I said, I don't want to have to make a different URL template and method for each object type. I'm hoping to have something like the following:
public String getRequest(String url, Object obj) {
String serializedUri = SERIALIZE_URI(obj);
String response = restTemplate.getForObject("URL?" + serializedUri, String.class);
return response;
}
where SERIALIZE_URI is where I'd handle it. And I could call it like getRequest("whatever", C1Object); and getRequest("whateverElse", C2Object);.
I think, solution number 4 is OK. It is simple to understand and clear.
I propose similar solution in which we can use #JsonAnySetter annotation. Please, see below example:
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonProgram {
public static void main(String[] args) throws Exception {
C1 c1 = new C1();
c1.setProp1("a");
c1.setProp3("c");
User user = new User();
user.setName("Tom");
user.setSurname("Irg");
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.convertValue(c1, UriFormat.class));
System.out.println(mapper.convertValue(user, UriFormat.class));
}
}
class UriFormat {
private StringBuilder builder = new StringBuilder();
#JsonAnySetter
public void addToUri(String name, Object property) {
if (builder.length() > 0) {
builder.append("&");
}
builder.append(name).append("=").append(property);
}
#Override
public String toString() {
return builder.toString();
}
}
Above program prints:
prop1=a&prop2=null&prop3=c
name=Tom&surname=Irg
And your getRequest method could look like this:
public String getRequest(String url, Object obj) {
String serializedUri = mapper.convertValue(obj, UriFormat.class).toString();
String response = restTemplate.getForObject(url + "?" + serializedUri, String.class);
return response;
}
Lets we have c1.
c1.setC1prop1("C1prop1");
c1.setC1prop2("C1prop2");
c1.setC1prop3("C1prop3");
Converts c1 into URI
UriComponentsBuilder.fromHttpUrl("http://test.com")
.queryParams(new ObjectMapper().convertValue(c1, LinkedMultiValueMap.class))
.build()
.toUri());
After we will have
http://test.com?c1prop1=C1prop1&c1prop2=C1prop2&c1prop3=C1prop3
I'm using spring MVC for receiving a JSON from client and automatically create an object from it. The problem is that the client doesn't send to server all the fields that are in the entity, but some fields are null and overwrite existing values calling userDao.persist(user). For example, i have this entity:
#Entity
public class User {
#Id #GeneratedValue
private int id;
private String username;
private String password;
private String email;
But the user never send me the password, so the object built from JSON has "password" field empty. I don't want the password field to be overwritten by a null value. There's a way to say to hibernate "if you find a null value ignore it and don't overwrite the value that is saved in database?". I can't believe that there isn't a easy solution to this apparently simple problem.
I think the source of your problem is that the object you're getting back from your JSON parsing never had the actual values in it. It is a bean that has only the values set that are in your JSON.
You need to load your entity from the DB and then set the non-null fields from your JSON onto the loaded entity. That way only fields that are supplied in the JSON will be set.
I recommend an adapter of some sort to "merge" (not JPA merge) the DB version and the JSON version before saving the DB version.
Adding a #NotNull constraint and Bean Validation will make sure the values are not null when attempting to save. Unfortunately they won't help you get the values into the entity to save.
I have the same issue.
I solved it in this way.
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import java.lang.reflect.Field;
import java.util.Hashtable;
public class Updater {
private final static Logger log = LogManager.getLogger(Updater.class);
public static <E> E updater(E oldEntity, E newEntity) {
Field[] newEntityFields = newEntity.getClass().getDeclaredFields();
Hashtable newHT = fieldsToHT(newEntityFields, newEntity);
Class oldEntityClass = oldEntity.getClass();
Field[] oldEntityFields = oldEntityClass.getDeclaredFields();
for (Field field : oldEntityFields){
field.setAccessible(true);
Object o = newHT.get(field.getName());
if (o != null){
try {
Field f = oldEntityClass.getDeclaredField(field.getName());
f.setAccessible(true);
log.info("setting " + f.getName());
f.set(oldEntity, o);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
return oldEntity;
}
private static Hashtable<String, Object> fieldsToHT(Field[] fields, Object obj){
Hashtable<String,Object> hashtable = new Hashtable<>();
for (Field field: fields){
field.setAccessible(true);
try {
Object retrievedObject = field.get(obj);
if (retrievedObject != null){
log.info("scanning " + field.getName());
hashtable.put(field.getName(), field.get(obj));
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return hashtable;
}
}
It is clearly a workaround but it seems to work smoothly... in the next days I think I'll write the recursive part.
Implement setters for you attributes and do the checks there.
Check Hibernate Validation project, which can be used to verify your object on DAO level, as well as on Spring Web layer.
I wrote this answer while being an unexpirienced studen. Today my answer would be similar to the one from #James DW. Also, from the term userDao, I assume that it is some kind of ORM/ODM. In that case it is definitly worth searching "pros and cons of ORM/ODM".
original answer (which was accepted back in 2011):
If your problem is only the database, then I suggest you use a stored procedure, which checks if that value is null, and then dose not change the existing value. That way you can still send a null value, and your validation is on server side which is more robust.
I have a java class which has one field with getter and setter, and a second pair of getter and setter that access this field in another way:
public class NullAbleId {
private static final int NULL_ID = -1;
private int internalId;
getter & setter for internalId
public Integer getId() {
if(this.internalId == NULL_ID) {
return null;
} else {
return Integer.valueOf(internalId);
}
}
public void setId(Integer id) {
if (id == null) {
this.internalId = NULL_ID;
} else {
this.internalId = id.intValue();
}
}
}
(the reason for this construction is that I want to build a way to hande Nullable Intergers)
On the Flash/Flex client side, I have a Class with two properties: id and internalId (the id properties are only for testing, at the end they should return the internalId value)
BlazeDS seams to transfer both values: id and internalId, because both have a complete getter setter pair. I want Blaze not to transfer id, only internalId should be transferred. But I have no idea how I have to configure that.
All the rules for BlazeDS serialization are here:
http://livedocs.adobe.com/blazeds/1/blazeds_devguide/help.html?content=serialize_data_3.html
Here is a quote: "Fields that are static, transient, or nonpublic, as well as bean properties that are nonpublic or static, are excluded."
So if you can make your id property fit that criteria it will be excluded. Another option would be to create a custom serializer that overtly does not include your id property.
All the best,
~harris
Besides transient / marshaller you can implement the Externalizable interface and create your custom serialization.
See serialization rules
It maybe a little bit old, but it could help some : there is a nice ticket about excluding properties from Java to Flex via BlazeDS
EDIT : a better soluce, it's to use the #AmfIgnore (or #AmfIgnoreField if your serialization is directly on the fields) annotation present in the spring-flex-core.jar (I've used the 1.5.2-RELEASE)