Java REST service accepts POJO, but fields are always null - java

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.

Related

How the swagger works in java backend? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 months ago.
Improve this question
I am a frontend developer. The backend developer left his job. So, I will also work on the backend part. I do not have any experience in the backend development. I am analyzing the backend code. I have few questions. I would like to clear my concepts.
I have attached the Java code file.
1- What are these imports for?:
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
2- why we put #JsonProperty before declaring each variable?
3- why we put class name in few methods? such as:
public LedgerAccountRequestDto taxRateId(String taxRateId) {
this.taxRateId = taxRateId;
return this;
}
4- What is the use of:
#ApiModel(description = "transaction request")
#Validated**
5- What is the use of:
#ApiModelProperty(required = true, value = "")
#NotNull
6- What is hashCode() method actually doing?
public int hashCode() {
return Objects.hash(name, number, typeId, taxRateId);
}
Please help me in understanding these concepts. Thank you very much
package com.kin.account.api.ledgerAccount.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.NotNull;
import java.util.Objects;
/**
* transaction request
*/
#ApiModel(description = "transaction request")
#Validated
public class LedgerAccountRequestDto {
#JsonProperty("name")
private String name = null;
#JsonProperty("number")
private String number = null;
#JsonProperty("typeId")
private String typeId = null;
#JsonProperty("taxRateId")
private String taxRateId = null;
public LedgerAccountRequestDto name(String name) {
this.name = name;
return this;
}
/**
* Get name
* #return name
**/
#ApiModelProperty(required = true, value = "")
#NotNull
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public LedgerAccountRequestDto number(String number) {
this.number = number;
return this;
}
/**
* Get number
* #return number
**/
#ApiModelProperty(required = true, value = "")
#NotNull
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public LedgerAccountRequestDto typeId(String typeId) {
this.typeId = typeId;
return this;
}
/**
* Get typeId
* #return typeId
**/
#ApiModelProperty(required = true, value = "")
#NotNull
public String getTypeId() {
return typeId;
}
public void setTypeId(String typeId) {
this.typeId = typeId;
}
public LedgerAccountRequestDto taxRateId(String taxRateId) {
this.taxRateId = taxRateId;
return this;
}
/**
* Get taxRateId
* #return taxRateId
**/
#ApiModelProperty(required = true, value = "")
#NotNull
public String getTaxRateId() {
return taxRateId;
}
public void setTaxRateId(String taxRateId) {
this.taxRateId = taxRateId;
}
#Override
public boolean equals(java.lang.Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
LedgerAccountRequestDto ledgerAccountRequestDto = (LedgerAccountRequestDto) o;
return Objects.equals(this.name, ledgerAccountRequestDto.name) &&
Objects.equals(this.number, ledgerAccountRequestDto.number) &&
Objects.equals(this.typeId, ledgerAccountRequestDto.typeId) &&
Objects.equals(this.taxRateId, ledgerAccountRequestDto.taxRateId);
}
#Override
public int hashCode() {
return Objects.hash(name, number, typeId, taxRateId);
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("class LedgerAccountRequestDto {\n");
sb.append(" name: ").append(toIndentedString(name)).append("\n");
sb.append(" number: ").append(toIndentedString(number)).append("\n");
sb.append(" typeId: ").append(toIndentedString(typeId)).append("\n");
sb.append(" taxRateId: ").append(toIndentedString(taxRateId)).append("\n");
sb.append("}");
return sb.toString();
}
/**
* Convert the given object to string with each line indented by 4 spaces
* (except the first line).
*/
private String toIndentedString(java.lang.Object o) {
if (o == null) {
return "null";
}
return o.toString().replace("\n", "\n ");
}
}
I will try to make this very simple and take you step by step
1. What are these imports for?:
import com.fasterxml.jackson.annotation.JsonProperty;
To import java package into a class, we need to use java import keyword which is used to access package and its classes into the java program. Use import to access built-in and user-defined packages into your java source file so that your class can refer to a class that is in another package by directly using its name. So using the above example import com.fasterxml.jackson.annotation.JsonProperty, that line imports the JsonProperty annotation from the jackson library.
2 Why use #JsonProperty before declaring each variable?
#JsonProperty
The #JsonProperty annotation is used to map property names with JSON keys during serialization and deserialization. By default, if you try to serialize a POJO, the generated JSON will have keys mapped to the fields of the POJO. If you want to override this behavior, you can use the #JsonProperty annotation on the fields. It takes a String attribute that specifies the name that should be mapped to the field during serialization.
3 Why we use class names in methods? such as:
public LedgerAccountRequestDto taxRateId(String taxRateId) {
this.taxRateId = taxRateId;
return this;
}
The above is a setter method called narrated with type LedgerAccountRequestDto. It's just the same as having a similar example with the type String. In the code above, the method returns the class instance as the return type.
4 What is the use of:
#ApiModel(description = "transaction request")
#Validated**
#ApiModel is a Swagger annotation.
Swagger is the standard way of documenting the Standard APIs. Swagger is helpful when deploying APIs in azure. Swagger is primarily used for documenting API. for the other developers to be able to use the API, the API must be properly documented; otherwise, how would they know that what are the endpoints exposed by the api and what are the operations supported on those endpoints? What parameters should they pass, and what will they get back? What authentication methods to use?. To answer these questions, it is very important to document the APIs; if you want APIs to be consumed and properly used. To learn more about Swagger, check Swagger - Javatpoint and Swagger - Github repo
#ApiModel - Provides additional information about Swagger models. Swagger-core builds the model definitions based on the references to them throughout the API introspection. The #ApiModel allows you to manipulate the metadata of a model from a simple description or name change to a definition of polymorphism.
#Valid and #Validated Annotations - In Spring, we use JSR-303's #Valid annotation for method level validation. We also use it to mark a member attribute for validation. However, this annotation doesn't support group validation.
Groups help to limit the constraints applied during validation. One particular use case is UI wizards. In the first step, we may have a certain sub-group of fields. In the subsequent step, there may be another group belonging to the same bean. So we need to apply constraints on these limited fields in each step, but #Valid doesn't support this.
In this case, for group-level, we have to use Spring's #Validated, which is a variant of JSR-303's #Valid. This is used at the method level. For marking member attributes, we continue to use the #Valid annotation.
5 What is the use of:
#ApiModelProperty(required = true, value = "")
#NotNull
#ApiModelProperty - In Swagger, this Adds and manipulates data of a model property. The #ApiModelProperty allows controlling Swagger-specific definitions such as allowed values, and additional notes. It also offers additional filtering properties in case you want to hide the property in certain scenarios.
The required parameter specifies if the parameter is required or not. The value parameter defines a brief description of this property.
The #NonNull is a common Spring annotation to declare that annotated elements cannot be null. It denotes that a parameter, field, or method return value can never be null. This is a marker annotation and it has no specific attributes.
6 What is hashCode() method actually doing?
public int hashCode() {
return Objects.hash(name, number, typeId, taxRateId);
}
The hashCode method is an inbuilt method that returns the integer hashed value of the input value. To properly understand hashCode() and equals() using examples, check out HashCode() in Java - scaler.com and also What is the hashCode method in Java? - educative.io
ADVICE
You have to learn Java if you don't have the basics, researching this way will be so difficult. Check the below resource links to learn Java and Spring boot.
Learn Java
Notes - Learn Java - By Jakob Jenkov
Videos - Learn Java - By Java Guides
Learn Spring Boot
Notes - Learn SpringBoot - By Javatpoint
Videos - Learn SpringBoot - By Java Guides

How to have query string parameter(s) and field name(s) different

I have a controller which will have 3 query strings.
Instead of having 3 fields in controller, I am defining them in class.
public class PassengerInformation{
String travellerAddress;
String travellerAge;
String travellerName;
}
Now in controller , I am able to accept them
#GetMapping("/passenger-info)
public TravelInformation getPassengerInfo(PassengerInformation info){
//Call a service
}
Now, this works as expected, if I pass the query string as is. eg: /passenger-info?travellerAge=21.
But, How do I accept the query parameter names different to it's corresponding fieldName.
I should be able to call it as below:
/passenger-info?traveller_age=21&traveller_name=JohnWick&traveller_address=ST.
Try to add the following constructor to your class
public class PassengerInformation{
String travellerAddress;
String travellerAge;
String travellerName;
#ConstructorProperties({"traveller_address", "traveller_age", "traveller_name"})
public PassengerInformation(String travellerAddress, String travellerAge, String travellerName) {
this.travellerAddress = travellerAddress;
...
}
}
The best you can do by the default features without any customisation is to use #ConstructorProperties :
public class PassengerInformation {
String travellerAddress;
String travellerAge;
String travellerName;
#ConstructorProperties({ "traveller_address", "traveller_age", "traveller_name" })
public PassengerInformation(String travellerAddress, String travellerAge, String travellerName) {
this.travellerAddress = travellerAddress;
this.travellerAge = travellerAge;
this.travellerName = travellerName;
}
}
This behaviour is mentioned at the docs as follows :
The #ModelAttribute parameter instance (i.e PassengerInformation)
is sourced in one of the following ways:
Retrieved from the model where it may have been added by a
#ModelAttribute method.
Retrieved from the HTTP session if the model attribute was listed in
the class-level #SessionAttributes annotation.
Obtained through a Converter where the model attribute name matches
the name of a request value such as a path variable or a request
parameter (see next example).
Instantiated using its default constructor.
Instantiated through a “primary constructor” with arguments that match
to Servlet request parameters. Argument names are determined through
JavaBeans #ConstructorProperties or through runtime-retained parameter
names in the bytecode.
The caveat here is that you need to make sure there are no default constructor in the PassengerInformation :
public class PassengerInformation {
public PassengerInformation(){}
}

Mapping one custom Java field to many JSON fields using Jackson #JsonDeserializer

I have a java class representing a JSON using Jackson. All of the fields, with one exception, can be translated using no annotations at all. 1-to-1, simple translations (although some of them are nested POJOs).
#Data
#AllArgsConstructor
#NoArgsConstructor
public class MyPojo {
private String someString;
private AnotherPojo someOtherPojo;
//The problem child:
private Object value;
}
The field value which is an exception to this rule, can represent any JSON field matching value* where * is a wildcard of indefinite length. That means valueString or valueReference in JSON will be assigned to this field with the assertion that only one may be present.
{
"someString": "asdasdadasdsa",
"someOtherPojo": {
"someOtherProperty": "whatever"
},
"valueCodeableConcept": {
"text": "text value",
"coding": [
{
"code": "my-code"
}
]
}
}
Using a custom deserializer on the top-level class, I can scrape all of the fields from the root node (baseNode in the following example) that start with value and set the value field appropriately. That works great! However, in doing so, I now have to set every other field in this MyPojo class manually in my deserializer, and I have to put a custom copy of this deserializer on each POJO that uses a field like value*.
private Object parseValueX(JsonNode baseNode, DeserializationContext context) throws IOException {
//Find the concrete implementation referred to by the value[x] field
Set<String> concreteNames = new HashSet<>();
baseNode.fieldNames().forEachRemaining(name -> {
if (name.startsWith("value")) {
concreteNames.add(name);
}});
if (concreteNames.isEmpty()) {
return null;
}
if (concreteNames.size() > 1) {
throw JsonMappingException.from(context, "The field value[x] must have no more than one concrete " +
"implementation, ex: valueCode, valueCodeableConcept, valueReference");
}
String concreteName = concreteNames.stream().findFirst().orElseThrow(() -> new RuntimeException(""));
JsonNode jsonSource = baseNode.get(concreteName);
//...deserialize from jsonSource, solved, but not relevant to question...
}
To make this apply to any value* property on any POJO, I tried to move the deserializer to the value attribute in the POJO (whereas it's on the top-level resource now). The first flaw is that the deserializer isn't even invoked unless the JSON property exactly matches value. What I actually need is for the entire parent JSON resource to be passed to that field-specific deserializer, so that I may find the matching field and assign it -- OR -- I need to be able to have the deserializer on MyPojo only assign the one field value and allow the automatic deserialization to take care of the others. How do I do either of these?
For those curious about my motivation, I am implementing the HL7 FHIR Specification, which specifies generic attributes called value[x] (here's one example: https://www.hl7.org/fhir/extensibility.html#Extension) where [x] becomes the type of the resource.
I think a good fit for you problem is #JsonAnySetter. This method annotation tells Jackson to route unknown properties to it. the arg (in your case) is a Map containing the json tree of the unknown property. if I understand your code properly, the name of the value property contains the class name of the target Pojo. so once you have a class name, you can tell Jackson how to "deserialize" the map into an instance of the target class.
Here is an example based on the code from the question
public class MyPojo {
public String someString; // made properties into public for this example...
public AnotherPojo someOtherPojo;
public Object value;
#JsonAnySetter
public void setValue(String name, Object value) {
System.out.println(name + " " + value.getClass());
System.out.println(value);
// basic validation
if (name.startsWith("value") && value instanceof Map) {
String className = "com.company." + name.substring("value".length());
System.out.println(name + " " + value.getClass() + " " + className);
System.out.println(value);
try {
// nice of Jackson to be able to deserialize Map into Pojo :)
ObjectMapper mapper = new ObjectMapper();
this.value = mapper.convertValue(value, Class.forName(className));
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(this.value + " " + this.value.getClass());
}
}
}
public class AnotherPojo {
public String someOtherProperty;
}
public class CodeableConcept {
public String text;
public Code[] coding;
}
public class Code {
public String code;
}

Jackson does not use setter

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

Jackson: What happens if a property is missing?

What happens if I annotate a constructor parameter using #JsonProperty but the Json doesn't specify that property. What value does the constructor get?
How do I differentiate between a property having a null value versus a property that is not present in the JSON?
Summarizing excellent answers by Programmer Bruce and StaxMan:
Missing properties referenced by the constructor are assigned a default value as defined by Java.
You can use setter methods to differentiate between properties that are implicitly or explicitly set. Setter methods are only invoked for properties with explicit values. Setter methods can keep track of whether a property was explicitly set using a boolean flag (e.g. isValueSet).
What happens if I annotate a constructor parameter using #JsonProperty but the Json doesn't specify that property. What value does the constructor get?
For questions such as this, I like to just write a sample program and see what happens.
Following is such a sample program.
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.ObjectMapper;
public class JacksonFoo
{
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper();
// {"name":"Fred","id":42}
String jsonInput1 = "{\"name\":\"Fred\",\"id\":42}";
Bar bar1 = mapper.readValue(jsonInput1, Bar.class);
System.out.println(bar1);
// output:
// Bar: name=Fred, id=42
// {"name":"James"}
String jsonInput2 = "{\"name\":\"James\"}";
Bar bar2 = mapper.readValue(jsonInput2, Bar.class);
System.out.println(bar2);
// output:
// Bar: name=James, id=0
// {"id":7}
String jsonInput3 = "{\"id\":7}";
Bar bar3 = mapper.readValue(jsonInput3, Bar.class);
System.out.println(bar3);
// output:
// Bar: name=null, id=7
}
}
class Bar
{
private String name = "BLANK";
private int id = -1;
Bar(#JsonProperty("name") String n, #JsonProperty("id") int i)
{
name = n;
id = i;
}
#Override
public String toString()
{
return String.format("Bar: name=%s, id=%d", name, id);
}
}
The result is that the constructor is passed the default value for the data type.
How do I differentiate between a property having a null value versus a property that is not present in the JSON?
One simple approach would be to check for a default value post deserialization processing, since if the element were present in the JSON but had a null value, then the null value would be used to replace any default value given the corresponding Java field. For example:
import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility;
import org.codehaus.jackson.annotate.JsonMethod;
import org.codehaus.jackson.map.ObjectMapper;
public class JacksonFooToo
{
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper().setVisibility(JsonMethod.FIELD, Visibility.ANY);
// {"name":null,"id":99}
String jsonInput1 = "{\"name\":null,\"id\":99}";
BarToo barToo1 = mapper.readValue(jsonInput1, BarToo.class);
System.out.println(barToo1);
// output:
// BarToo: name=null, id=99
// {"id":99}
String jsonInput2 = "{\"id\":99}";
BarToo barToo2 = mapper.readValue(jsonInput2, BarToo.class);
System.out.println(barToo2);
// output:
// BarToo: name=BLANK, id=99
// Interrogate barToo1 and barToo2 for
// the current value of the name field.
// If it's null, then it was null in the JSON.
// If it's BLANK, then it was missing in the JSON.
}
}
class BarToo
{
String name = "BLANK";
int id = -1;
#Override
public String toString()
{
return String.format("BarToo: name=%s, id=%d", name, id);
}
}
Another approach would be to implement a custom deserializer that checks for the required JSON elements. And yet another approach would be to log an enhancement request with the Jackson project at http://jira.codehaus.org/browse/JACKSON
In addition to constructor behavior explained in #Programmer_Bruce's answer, one way to differentiate between null value and missing value is to define a setter: setter is only called with explicit null value.
Custom setter can then set a private boolean flag ("isValueSet" or whatever) if you want to keep track of values set.
Setters have precedence over fields, in case both field and setter exist, so you can "override" behavior this way as well.
I'm thinking of using something in the style of an Option class, where a Nothing object would tell me if there is such a value or not. Has anyone done something like this with Jackson (in Java, not Scala, et al)?
(My answer might be useful to some people finding this thread via google, even if it doesn't answer OPs question)
If you are dealing with primitive types which are omittable, and you do not want to use a setter like described in the other answers (for example if you want your field to be final), you can use box objects:
public class Foo {
private final int number;
public Foo(#JsonProperty Integer number) {
if (number == null) {
this.number = 42; // some default value
} else {
this.number = number;
}
}
}
this doesn't work if the JSON actually contains null, but it can be sufficient if you know it will only contain primitives or be absent
another option is to validate the object after deserialization either manually or via frameworks such java bean validation or, if you are using spring, the spring validation support.

Categories