JPA / Jackson - Exclude fields when deserialize and include them when serialize - java

I have a JPA entity with a couple of fields (the real ones are more complex). I'm receiving some data via REST (POST operation in a Spring controller) and storing it right away in the JPA entities; I want to see if there is a possibility to exclude some field(s) when the request is sent, Jackson deserializes it, and constructs the object. But at the same time I want those fields to be included when I send back (object gets serialized) the response.
#Table("key_card")
public final class KeyCard {
private String username; // Don't want this to be sent as input,
// but want to be able to send it back
// in the response
#NotBlank
private final char[] password;
}
I'm just trying not to model it twice (for the request and response) if there is a way to solve this.

You can use JSON views: http://wiki.fasterxml.com/JacksonJsonView
Class Views {
static class AlwaysInclude { }
static class OnlyOnSerialize extends AlwaysInclude { }
}
And then on your view:
#Table("key_card")
public final class KeyCard {
#JsonView(Views.OnlyOnSerialize.class)
private String username;
#JsonView(Views.AlwaysInclude.class)
#NotBlank
private final char[] password;
}

To exclude a Java object property only from Json deserialization and to include instead its value during serialization you can use an appropriate combination of #JsonIgnore and #JsonProperty annotations.
In particular you should:
annotate with #JsonIgnore the property itself
annotate with #JsonIgnore its set method
annotate with #JsonProperty its get method
Here you can find an in-depth explanation and an example: Jackson: using #JsonIgnore and #JsonProperty annotations to exclude a property only from JSON deserialization

Related

Spring restTemplate .postForObject() mapping cannot access element

Im trying to use the restTemplate.postForObject(URL, Session.class) method and map the response to a POJO. This works partially, however when i try to access an element with a name like "name-with-dashes" I cannot find the element.
The JSON I am extracting from the method call:
{"age":60,"expire":12345,"name-with-dashes":"This name has dashes?!"...}
Here is the POJO im using to extract this data:
#Getter
#Setter
#JsonIgnoreProperties(ignoreUnknown = true)
public class Session {
private int age;
private long expire;
//will not grab name-with-dashes... returns null
private String nameWithDashes;
}
You should annotate your fields, especially the ones that do not comply to bean naming conventions, with the #JsonProperty annotation as follows:
#JsonProperty("name-with-dashes")
private String nameWithDashes;
You can annotate the property
#SerializedName("name-with-dashes")
private String nameWithDashes;
using Gson

#JsonIgnore only for response but not for requests

Is there a way to make #JsonIgnore annotation that it will only ignore during API or HTTP response but not ignore when doing API request.
I also understand that Jackson is used with several frameworks like Restlet, Spring, etc. so what is the generic way of doing this with the ignore annotation. The annotation class does not seem to have any parameters to set this.
Consider the code below:
public class BoxModel extends Model {
#JsonIgnore
private String entityId;
#JsonIgnore
private String secret;
}
In this example, the "secret" field should not be ignored during an API request but should not return back when doing a response, e.g. a JSON response. setting this field to null does not make the field go away, it just sets the value to null and so the field is still on the response payload.
Actually, the standard way is to have 2 separate classes for request and response, so you won't have any problem at all.
If you really need to use the same class for both cases, you can put #JsonInclude(Include.NON_NULL) onto the field instead of #JsonIgnore and set secret = null; before returning the response (as you said in question) - nullable field will be hidden after that. But it's some kind of a trick.
You could potentially find a way to achieve this using Jackson JSON Views by hiding fields when serializing the object.
Example
public class Item {
#JsonView(Views.Public.class)
public int id;
#JsonView(Views.Public.class)
public String itemName;
#JsonView(Views.Internal.class)
public String ownerName;
}
#JsonView(Views.Public.class)
#RequestMapping("/items/{id}")
public Item getItemPublic(#PathVariable int id) {
return ItemManager.getById(id);
}

Springboot: Can DTO be changed at runtime with null values not being present in the object returned from api?

I have a springboot application which is hitting raw api's of the datasource. Now suppose I have a Customer entity with approx 50 fields and I have a raw api for it in which I pass names of the columns and I get the values for that column. Now I am implementing api in springboot which consumes raw api.
I need to implement different api's in springboot for different combinations of the fields of Customer entity and return only those fields setted in object for which user had queried and remove the null valued fields from the object. One way is to implement different dto's for different combinations of the columns of Customer entity. Is there any other way to implement the same in which I don't need to define different dto's for different combinations of the columns of Customer entity in Spring boot ???
You can configure the ObjectMapper directly, or make use of the #JsonInclude annotation:
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
OR
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Customer {
private Long id;
private String name;
private String email;
private String password;
public Customer() {
}
// getter/setter ..
}
You can see how to do it with this sample code:
Customer customer = new Customer();
customer.setId(1L);
customer.setName("Vikas");
customer.setEmail("info#vikas.com");
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
String valueAsString = objectMapper.writeValueAsString(customer);
Since the password is left null, you will have an object that does not exist password.
{
"id": 1,
"name": "Vikas",
"email": "info#vikas.com"
}
with Jackson 2.0 serialization you can specify data inclusion on non nulls at different levels, i.e. on the object mapper (with constructor options), the DTO class or DTO class fields (with annotations).
See Jackson annotations here
This can be done using #JsonInclude inside the DTO class. Please refer following code block for ignoring null values.
#JsonInclude(Include.NON_NULL) // ignoring null values
#Data //lombock
#Builder //builder pattern
public class Customer {
private Long id;
private String name;
private String email;
private String password;
}

Issue with deserializing JSON using Jackson

I have such JSON
{"body":{"result":[{"crossStateId":1,"raceId":181564,"withOfficer":1,"documents":[{"indexed":0,"documentNumber":"zzz","isMain":1,"documentTypeId":6,"serverId":16,"countryId":327,"useDate":"2017-02-07T19:31:51.000+0000","documentSubTypeId":6,"crossId":5018177,"documentId":44973231,"personId":222,"infinity":0,"documentValid":"2023-08-25T20:00:00.000+0000"}],"directionId":2,"documentNumber":"sss","operatorUsername":"AIRPORT_84","crossDate":"2017-02-07T19:31:51.000+0000","serverId":16,"crossTypeId":1,"crossRegisterDate":"2017-02-07T19:31:52.818+0000","officerNote":"","children":[],"personNote":"","crossId":5018177,"workplaceId":82,"divisionId":2,"race":{"carriageContainer":0,"raceId":181564,"raceStateId":1,"directionId":2,"creatorId":415,"countryId":327,"transportIdByType":605,"raceDateTime":"2017-02-07T19:20:58.000+0000","raceNumber":"841 sss sss","creatorUsername":"AIRPORT_8","divisionId":2,"transportTypeId":3,"createDate":"2017-02-07T19:20:58.000+0000"},"syncState":0,"autos":[],"userId":491,"raceNumber":"841 sss sss","operatorNote":"","person":{"firstNameEn":"JUMBERI","indexed":1,"lastNameGe":"ჩოხელი","genderId":2,"personId":6027803,"personalNumber":"222","countryNameGe":"sss","birthDate":"1963-06-14T20:00:00.000+0000","lastNameEn":"sss","countryId":327,"firstNameGe":"sss"},"airplane":{"raceNumber":"841 sss sss","airCompanyId":1,"airplaneId":605,"airportId":5657,"bortNumber":"01","transportSubTypeId":78,"countryId":360},"underAge":0,"personId":6027803,"decisionId":22}],"total":8264},"errorCode":0}
I would like to deserialize it to Java class but I am interested in only some JSON fields. Anyway here are the model classes:
public class Response implements Serializable {
private Body body;
private long errorCode;
}
public class Body implements Serializable {
Result result[];
}
public class Result implements Serializable {
private long crossStateId;
private long raceId;
private Person person;
private Child children [];
private Auto autos[];
}
etc.
But for some reason I get following exception:
org.codehaus.jackson.map.exc.UnrecognizedPropertyException:
Unrecognized field "body" (Class com.demo.Response), not marked as
ignorable at [Source: java.io.StringReader#6483f5ae; line: 1, column:
10] (through reference chain: com.demo.Response["body"])
Here is code(the JSON string is correctly received and has same format as I initially mentioned in the beginning):
String res = MainProgram.sendGet("someURL");
ObjectMapper objectMapper = new ObjectMapper();
Response ob = objectMapper.readValue(res, Response.class);
I would appreciate some help.
You need to create getters and setters for the fields, and you should add annotations to your fields.
Annotation:
#JsonProperty(value = "body")
private Body body;
Doing one of above will make it work.
Sidenote:
You can create your pojos from json automatically with http://www.jsonschema2pojo.org/. Just paste it in and download it, or use one of their plugins.
As mentioned by others, private fields are not auto-detect by default, so either:
Annotating fields with #JsonProperty OR
Adding setter
is needed for deserialization.
However, there is another possibility: you can use annotations #JsonAutoDetect to change minimum visibility needed, and here enable discovery of ALL fields.
Or you can even change the defaults used via ObjectMapper method (something like setVisibility(...)).

Want to hide some fields of an object that are being mapped to JSON by Jackson

I have a User class that I want to map to JSON using Jackson.
public class User {
private String name;
private int age;
private int securityCode;
// getters and setters
}
I map this to a JSON string using -
User user = getUserFromDatabase();
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(user);
I don't want to map the securityCode variable. Is there any way of configuring the mapper so that it ignores this field?
I know I can write custom data mappers or use the Streaming API but I would like to know if it possible to do it through configuration?
You have two options:
Jackson works on setters-getters of fields. So, you can just remove getter of field which you want to omit in JSON. ( If you don't need getter at other place.)
Or, you can use the #JsonIgnore annotation of Jackson on getter method of that field and you see there in no such key-value pair in resulted JSON.
#JsonIgnore
public int getSecurityCode(){
return securityCode;
}
Adding this here because somebody else may search this again in future, like me. This Answer is an extension to the Accepted Answer
You have two options:
1. Jackson works on setters-getters of fields. So, you can just remove getter of field which you want to omit in JSON. ( If you don't need getter at other place.)
2. Or, you can use the `#JsonIgnore` [annotation of Jackson][1] on getter method of that field and you see there in no such key-value pair in resulted JSON.
#JsonIgnore
public int getSecurityCode(){
return securityCode;
}
Actually, newer version of Jackson added READ_ONLY and WRITE_ONLY annotation arguments for JsonProperty. So you could also do something like this.
#JsonProperty(access = Access.WRITE_ONLY)
private String securityCode;
instead of
#JsonIgnore
public int getSecurityCode(){
return securityCode;
}
you also can gather all properties on an annotation class
#JsonIgnoreProperties( { "applications" })
public MyClass ...
String applications;
If you don't want to put annotations on your Pojos you can also use Genson.
Here is how you can exclude a field with it without any annotations (you can also use annotations if you want, but you have the choice).
Genson genson = new Genson.Builder().exclude("securityCode", User.class).create();
// and then
String json = genson.serialize(user);
Field Level:
public class User {
private String name;
private int age;
#JsonIgnore
private int securityCode;
// getters and setters
}
Class Level:
#JsonIgnoreProperties(value = { "securityCode" })
public class User {
private String name;
private int age;
private int securityCode;
}
if you are using GSON you have to mark the field/member declarations as #Expose and use the GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()
Don't forget to mark your sub classes with #Expose otherwise the fields won't show.
I suggest you use this.
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private int securityCode;
This allows you to set the value of securityCode(especially if you use lombok #Setter) and also prevent the field from showing up in the GET request.
I had a similar case where I needed some property to be deserialized (JSON to Object) but not serialized (Object to JSON)
First i went for #JsonIgnore - it did prevent serialization of unwanted property, but failed to de-serialize it too. Trying value attribute didn't help either as it requires some condition.
Finally, working #JsonProperty with access attribute worked like a charm.

Categories

Resources