I am trying to get a method in springmvc to accept a partial json string of an object, and have jackson automatically de-serialize it for me.
I can solve it by making a temporary object with only the attributes I want, or pass it in as a string and use Gson to desearialize it for me, but these feel hacky. Is there any way to tell jackson how to do it?
Here is the controller snippet:
#RequestMapping(value = "/task",
method = RequestMethod.POST,
consumes="application/json")
public #ResponseBody String postTask(#RequestBody Task task){
if(task.getId() == null){
task.setId(UUID.randomUUID());
}
if(task.getDate_entered() == 0){
task.setDate_entered(System.currentTimeMillis());
}
TaskDao.addTask(task);
return "success";
}
And the task, a basic pojo:
#JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
public class Task {
private UUID id;
private String name;
private String description;
private long date_entered;
private long finish_by;
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
// Rest of the getters and setters
}
If you can't tell by my other spring related questions, I'm kind of flying blind, and can't figure out the proper google query for this one.
Thanks!
You need to use #JsonIgnoreannotation of jackson on the method (on setter for deserialization and on getter for serialization) or field, for which you want to ignore serialization and/or deserialization. e.g.
In your example, if you don't want to serialize description, then you can do,
#JsonIgnore
public void setDescription(String description) {
this.description = description;
}
And you will see, that you won't get value of description in converted model.
Related
I have a model like this:
public class Employee {
#JsonProperty("emplyee_id")
private Integer id;
#JsonProperty("emplyee_first_name")
private String firstName;
#JsonProperty("emplyee_last_name")
private String lastName;
#JsonProperty("emplyee_address")
private String address;
#JsonProperty("emplyee_age")
private Byte age;
#JsonProperty("emplyee_level")
private Byte level;
//getters and setters
}
now I need to create two JSONs using this (only) model.
the first one must like this for example:
{
"employee_id":101,
"employee_first_name":"Alex",
"employee_last_name":"Light",
"employee_age":null,
"employee_address":null
}
and the second one must like this for example:
{
"employee_id":101,
"employee_level":5
}
by the way, I already tested #JsonIgnore and #JsonInclude(JsonInclude.Include.NON_NULL).
the problem of the first one (as much as I know) is, those fields can't be included in other JSONs (for example if level get this annotation, it won't be included in the second JSON)
and the problem of the second one is, null values can't be included in JSON.
so can I keep null values and prevent some other property to be included in JSON without creating extra models? if the answer is yes, so how can I do it? if it's not I really appreciate if anyone gives me the best solution for this state.
thanks very much.
it could be useful for you using #JsonView annotation
public class Views {
public static class Public {
}
public static class Base {
}
}
public class Employee {
#JsonProperty("emplyee_id")
#JsonView({View.Public.class,View.Base.class})
private Integer id;
#JsonProperty("emplyee_first_name")
#JsonView(View.Public.class)
private String firstName;
#JsonProperty("emplyee_last_name")
#JsonView(View.Public.class)
private String lastName;
#JsonProperty("emplyee_address")
private String address;
#JsonProperty("emplyee_age")
private Byte age;
#JsonProperty("emplyee_level")
#JsonView(View.Base.class)
private Byte level;
//getters and setters
}
in your json response add #JsonView(Public/Base.class) it will return based on jsonview annotations
//requestmapping
#JsonView(View.Public.class)
public ResponseEntity<Employee> getEmployeeWithPublicView(){
//do something
}
response:
{
"employee_id":101,
"employee_first_name":"Alex",
"employee_last_name":"Light",
"employee_age":null,
"employee_address":null
}
for the second one
//requestmapping
#JsonView(View.Base.class)
public ResponseEntity<Employee> getEmployeeWithBaseView(){
//do something
}
response
{
"employee_id":101,
"employee_level":5
}
I am using Spring #RequestBody to map a JSON payload to a Java Object. Unfortunately this JSON payload does not use a set convention but rather has names that use both camelCase and snake_case.
To be clear my Controller looks like this:
#RequestMapping(value="/mobile/device", method = RequestMethod.PUT)
public ResponseEntity<Object> flagDevice (#RequestBody List<MobileDeviceData> deviceInfoList) {
... code here ...
}
with the MobileDeviceData Entity object having several setter methods like:
public void setDeviceName(String deviceName) {
this.deviceName = deviceName;
}
public void setFlagId(int flagId) {
this.flagId = flagId;
}
This works great and without any extra effort when the JSON objects name is camelCase. However for snake_case names I need to add the Annotation:
#JsonProperty("flag_id")
private int flagId;
in order for it to be picked up.
I know it's not a good idea to use the #JsonProperty if it can be avoided as you then will need to annotate every parameter. My question is, is there a more general way to enforce matching snake_case with the corresponding camelCase in the Entity object? And obviously to do it without screwing up the ones that are already camelCase.
As per the article here, there is a simple approach to deserialize the MobileDeviceData class. Here is the sample code as below:
#JsonDeserialize(using = UserDeserializer.class)
public class User {
private ObjectId id;
private String username;
private String password;
public User(ObjectId id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
public ObjectId getId() { return id; }
public String getUsername() { return username; }
public String getPassword() { return password; }
}
Assume User is the class we’re interested in writing the Deserializer for. Not much is notable here, except for the annotations that tell Jackson who knows how deserialize this class.
public class UserDeserializer extends JsonDeserializer {
#Override
public User deserialize(JsonParser jsonParser,
DeserializationContext deserializationContext) throws IOException {
ObjectCodec oc = jsonParser.getCodec();
JsonNode node = oc.readTree(jsonParser);
return new User(null,
node.get("username").getTextValue(),
node.get("password").getTextValue());
}
}
The deserializer is created by extending Jackson’s abstract JsonDeserializer class, and giving it the type we want to deserialize to. Difficult is figuring out that you can reference the JSON by field name with the JsonParser's ObjectCodec.
I hope it helps.
Please feel free to comment if needed!
Having been working on this a bit, I now realize doing anything like what was requested would be counterproductive.
When you receive (deserialize) a JSON Object, it is generally expected that you will deliver (serialize) with the same parameters. If an implementation extracted both camelCase and underscore parameters the same way, then it would not know how to deserialize correctly later on. By following a standard convention and then using #JsonProperty for all the exceptions, it remains possible to deserialize and later deliver the JSON object just as it was received.
I am using Jackson to serialize and deserialize data for a RESTful API. I'd like to have a REST resource (/comments) that allows to POST comments as well as to GET a list of comments.
Here's a (simplified) example of what gets posted to /comments.
{"text":"Text","author":"Paul","email":"paul#example.org"}
Here's what the result of GET /comments should look like:
[{"text":"Text","author":"Paul","emailHash":"76w0kjKP9HpsdhBjx895Sg=="}]
Since email addresses shouldn't be visible to anyone, I decided to return only a MD5 hash of the email addresses in the response.
I have created a simple POJO class Comment that has fields with getters and setters for text, author, email, and emailHash.
Now, when I serialize the result, what I get is the following:
[{"text":"Text","author":"Paul","email":null,"emailHash":"76w0kjKP9HpsdhBjx895Sg=="}]
But I really don't like email to be returned as null here. It rather shouldn't be included at all.
Using the annotation #JsonIgnore on that field will also ignore it on deserialization. Do I have to create two classes, say CreationComment and ResultComment with a super-class Comment that shares common fields or is there a way that avoids creating additional classes?
You don't have to create 2 classes at all. With Jackson you have full control of the behavior of a property during serialization and deserialization using annotations, with #JsonIgnorein the getter you prevent the property from being serialized in your Json response and using #JsonProperty annotation in the setter the property will be set during deserialization. The code will look like this:
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Comment {
private String author;
private String email;
#JsonIgnore
public String getEmail() {
return email;
}
#JsonProperty
public void setEmail(String email) {
this.email = email;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public static void main(String[] args) {
ObjectMapper objectMapper = new ObjectMapper();
Comment comment = new Comment();
comment.setAuthor("anAuthor");
comment.setEmail("email#example.com");
try {
System.out.println(objectMapper.writeValueAsString(comment));
String json = "{\"author\":\"anAuthor\",\"email\":\"another#email.com\"}";
Comment fromJson = objectMapper.readValue(json, Comment.class);
System.out.println("Result from Json: author= " + fromJson.getAuthor() + ", email= " + fromJson.getEmail());
} catch (Exception e) {
e.printStackTrace();
}
}
}
The output after running the main() method to test the solution:
{"author":"anAuthor"}
Result from Json: author= anAuthor, email= another#email.com
Hope it helps,
Jose Luis
You can put #JsonIgnore on getEmail to prevent it from being serialized to JSON and use #JsonCreator to indicate to Jackson a constructor to use for deserialization. The constructor would then only accept an email property and would hash and assign to your emailHash field.
You can put a #JsonInclude annotation on your Comment class to prevent serialization of null fields too.
Your class would probably end up looking something like this:
#JsonInclude(Include.NON_NULL)
public class Comment {
private final String email;
private final String emailHash;
#JsonCreator
public Comment(#JsonProperty("email") String email) {
this.email = email;
this.emailHash = MD5.hash(email);
}
#JsonIgnore
public String getEmail() {
return email;
}
public String getEmailHash() {
return emailHash;
}
}
I'm trying to use Gson to map JSON to POJO where the POJO contains a custom field that is not part of JSON. The field gets updated when the setters of other fields are invoked to add the names of the fields that are being updated to a list.
The POJO class looks something like this:
public class myPojo {
private List<String> dirtyFields;
private String id;
private String subject;
private String title;
public myPojo() {
dirtyFields = new ArrayList<String>();
}
public getId() {
return id;
}
public setId(String id) {
this.id = id;
}
public getSubject() {
return subject;
}
public setSubject(String subject) {
this.subject = subject;
dirtyFields.add("subject");
}
// more setter/getters
}
The dirtyFields ivar is not a deserialized field but it is used to keep track of the fields that are being updated.
After mapping, however, the list seems to become an empty list. This was not the case with Jackson.
Is this due to the expected Gson behaviour?
Gson does not call setter/getters during deserialization/serialization. It access, instead, directly to fields (even if private/protected) using reflection. This explains why your dirtyFields ivar is empty.
The possibility of calling setter/getters is not implemented in Gson as far as I know. The reason why Gson acts like this is explained better here. A comparison between Jackson and Gson features can be found here, you may be interested in setter/getter part.
However Gson is quite flexible to add a custom behavior to get what you need, you should start reading Read and write Json properties using methods (ie. getters & setters) bug
Another way to calculate your dirtyFields list could be using reflection and checking if every field of your POJO is null or not. You could start from this.
I'm using restTemplate to make a rquest to a servlet that returns a very simple representation of an object in json.
{
"id":"SomeID"
"name":"SomeName"
}
And I have a DTO with those 2 fields and the corresponding setters and getters.
What I would like to know is how to create the object using that json response
without having to "parse" the response.
Personally I would recommend Jackson. Its fairly lightweight, very fast and requires very little configuration. Here's an example of deserializing:
#XmlRootElement
public class MyBean {
private String id;
private String name;
public MyBean() {
super();
}
// Getters/Setters
}
String json = "...";
MyBean bean = new ObjectMapper().readValue(json, MyBean.class);
Here's an example using Google Gson.
public class MyObject {
private String id;
private String name;
// Getters
public String getId() { return id; }
public String getName() { return name; }
}
And to access it:
MyObject obj = new Gson().fromJson(jsonString, MyObject.class);
System.out.println("ID: " +obj.getId());
System.out.println("Name: " +obj.getName());
As far as the best way, well that's subjective. This is one way you can accomplish what you need.
http://code.google.com/p/json-simple/ is nice and lightweight for this