How to configure Jackson json deserializer such that special characters say "##", "#null", "-" to null
For example:
Say my json is
{ "userId":"kp","fname":"k","lname":"p","mname":"##", rndNum:"-"}
And Java class is
class User{
private String userId;
private String fname;
private String lname;
private String mname;
private Integer rndNum;
//getters/ and setters
}
I would expect output to be
user.getUserId() = kp
user.getMname() = null
user.getRndNum() = null
The simplest way would be to use a constructor and a validator for that. e.g.
public static String validate(String name) {
if (name != null && !name.isEmpty() && (name.charAt(0) == '#' || name.charAt(0) == '-')) {
return null;
}
return name;
}
class User {
User(String userId, String fname, String lname, String mname) {
this.userId = validate(userId);
this.fname = validate(fname);
this.lname = validate(lname);
this.mname = validate(mname);
}
I also think this properly separates functionality. Jackson code and annotations should handle deserialization of whatever happens to be in the JSON. Your class should know if values used for initialization is valid regardless where they come from.
There are two ways to do this depending on how advanced solution you need:
There is an easy way if you are willing to register your types one-by-one. In this solution you are providing the deserialization code from raw Jackson objects to your desired output.
Or you can go with the more powerful solution which overrides the default Serializer by using a custom provider to cover all types. Here you are defining how Strings should be deserialized in general.
Related
I know there's lots of questions about skipping fields with a null value when serializing objects to JSON.
I want to skip / ignore fields with null values when deserializing JSON to an object.
Consider the class
public class User {
Long id = 42L;
String name = "John";
}
and the JSON string
{"id":1,"name":null}
When doing
User user = gson.fromJson(json, User.class)
I want user.id to be '1' and user.name to be 'John'.
Is this possible with either Gson or Jackson in a general fashion (without special TypeAdapters or similar)?
A lot of time gone by, but if you like me ran into this question and you are using at least Jackson 2.9 then one way you could sovle it is using JsonSetter and Nulls.SKIP:
public class User {
private Long id = 42L;
#JsonSetter(Nulls.SKIP)
private String name = "John";
... cooresponding getters and setters
}
This way, when null is encountered, setter will not be called.
Note: more details can be found here.
What i did in my case is to set a default value on the getter
public class User {
private Long id = 42L;
private String name = "John";
public getName(){
//You can check other conditions
return name == null? "John" : name;
}
}
I guess this will be a pain for many fields but it works in the simple case of less number of fields
To skip using TypeAdapters, I'd make the POJO do a null check when the setter method is called.
Or look at
#JsonInclude(value = Include.NON_NULL)
The annotation needs to be at Class level, not method level.
#JsonInclude(Include.NON_NULL) //or Include.NON_EMPTY, if that fits your use case
public static class RequestPojo {
...
}
For Deserialise you can use following at class level.
#JsonIgnoreProperties(ignoreUnknown = true)
Albeit not the most concise solution, with Jackson you can handle setting the properties yourself with a custom #JsonCreator:
public class User {
Long id = 42L;
String name = "John";
#JsonCreator
static User ofNullablesAsOptionals(
#JsonProperty("id") Long id,
#JsonProperty("name") String name) {
User user = new User();
if (id != null) user.id = id;
if (name != null) user.name = name;
return user;
}
}
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 know it's possible to ignore fields if they are null or if they are empty, but is it possible to ignore a field, for example if it is a String, and contains a certain substring?
This is possible if you e.g. use a combination of #JsonIgnore and a Converter.
If you assume the following Person POJO:
#JsonInclude(JsonInclude.Include.NON_EMPTY)
public class Person {
private final String email;
private final String name;
public Person(final String name, final String email) {
this.name = name;
this.email = email;
}
// Will use special conversion before serializing
#JsonSerialize(converter = EmailConverter.class)
public String getEmail() {
return email;
}
// Will simply use default serialization
public String getName() {
return name;
}
}
In the POJO you define that only non-empty values should be included. Furthermore, it is declared that a specific converter is to be used for the email property. The converter can be defined like this:
public class EmailConverter extends StdConverter<String, String> {
#Override
public String convert(final String value) {
return Optional.ofNullable(value)
.filter(email -> email.length() > 0)
.filter(email -> email.contains("#"))
.orElse(null);
}
}
Note that the converter uses Optional which is a java-8 feature but any validation code will do just fine. When null is returned it is simply skipped since it was declared that way in the Person class.
For more info, check out the JavaDocs for Converter and #JsonSerialize.
I am rookie in Java Annotation and have been searching for applying single annotation on multiple variable simultaneously.
Code:
#Document(collection = "users")
public class User {
private ObjectId id;
#NotNull
private String email;
private String imageURL;
private String authToken;
private Date createdDate;
private Date updateDate;
private boolean isActivated;
private int credits;
.....getter/Setter Method
I want to apply #NotNull property on email, imageURL and authToken too. I can do it by writing #NotNull to each variable but not preferring. How to do it?
#NotNull annotation can be applied at element not at group of elements.
JavaDoc: The annotated element must not be null. Accepts any type.
If you really want to get away with boiler plate code, you can use frameworks like Lombok which can help you to certain extent.
Link : http://projectlombok.org/features/Data.html
OR you can use reflection to validate all the method.
for (Field f : obj.getClass().getDeclaredFields()) {
f.setAccessible(true); // optional
if (f.get(obj) == null) {
f.set(obj, getDefaultValueForType(f.getType()));
// OR throw error
}
}
Java does not support multiple annotation of this type. But you can write something like this
Create a class with annotated field.
Create setters and getters to access the field.
Create all your name,email field as instance of this class.
This way fields will implicitly annotated as NotNull.
public class NotNullString {
#NotNull
String str;
public void set(String str)
{
this.str = str;
}
public String get()
{
return this.str;
}
}
NotNullString name;
NotNullString email;