Retrieve #JsonProperty value set in an enum - java

How do you retrieve the value set in a #JsonProperty annotation?
I want to be able to test JSON values of a REST endpoint. I wanted to use the existing enum instead of hardcoding a string. I cannot seem to figure out how to get the value set in the #JsonProperty annotation. 😕
import com.fasterxml.jackson.annotation.JsonProperty;
public enum StatusType {
#JsonProperty("unknown")
UNKNOWN,
#JsonProperty("warning")
WARNING,
#JsonProperty("success")
SUCCESS,
#JsonProperty("error")
ERROR,
#JsonProperty("info")
INFO
}
Ideally I would want to do something like:
mvc.perform(get("/status"))
.andExpect(jsonPath("status").value(StatusType.INFO))

You can use the following (don't forget to handle the exceptions):
String value = StatusType.class.getField(StatusType.INFO.name())
.getAnnotation(JsonProperty.class).value();
Alternatively, depending on your needs, you could define your enum as follows, using #JsonValue:
public enum StatusType {
UNKNOWN("unknown"),
WARNING("warning"),
SUCCESS("success"),
ERROR("error"),
INFO("info");
private String value;
StatusType(String value) {
this.value = value;
}
#JsonValue
public String getValue() {
return value;
}
}
Then you can use:
String value = StatusType.INFO.getValue();

Related

Validating input datatype

I am using below DTO class with respective annotations and are working fine also. But when I send a integer value for name/reqID(which is a String datatype) fields, still it is executing without any error/exception. How to avoid it or validate the datatype of incoming fields.
public class RequestDTO {
#NotEmpty(message = "Please provide reqID")
private String reqID;
#NotEmpty(message = "Please provide name")
private String name;
private Map <String, String> unknownProperties;
public AccountDTO(){
this.unknownProperties = new HashMap<String, String>();
}
public AccountDTO(String reqID, String name){
this.reqID= reqID;
this.name = name;
this.unknownProperties = new HashMap<String, String>();
}
#JsonAnySetter
public void add(String key, String value) {
this.unknownProperties.put(key, value);
}
#JsonAnyGetter
public Map <String, String> getUnknownProperties() {
return unknownProperties;
}
//getters and setters
}
working for { "reqID" : 56, "name" : 674 }. Have to check the datatype/reject the request. Any help would be appreciable.
If you're using Spring boot, by default it uses Jackson to parse JSON. There's no configuration option within Jackson to disable this feature
Here you will find interesting approaches to solving this problem:
Disable conversion of scalars to strings when deserializing with Jackson
You can disable MapperFeature ALLOW_COERCION_OF_SCALARS which is enabled by default.
Then conversions from JSON String are not allowed.
Doc Details here
public static final MapperFeature ALLOW_COERCION_OF_SCALARS
When feature is disabled, only strictly compatible input may be bound:
numbers for numbers, boolean values for booleans. When feature is
enabled, conversions from JSON String are allowed, as long as textual
value matches (for example, String "true" is allowed as equivalent of
JSON boolean token true; or String "1.0" for double).
Or create a custom json deserializer for string overriding default serializer JsonDeserializer<String>.
You could validate the input you are getting. But this is not specific to your DTO so if you have some sort of Utilities class with static methods (think about having one if you don't) it's better if you add it there and grab it for any DTO that might need this validation.
The validation method would look something like this:
public static boolean isNumber(String in) {
try{
Integer.parseInt(in);
// log something useful here
return true;
} catch(NumberFormatException e) {
return false;
}
}
You could then use this method throw your own exception. Then handle that the way you'd need:
if (Utilities.isNumber(reqID)){
throw new IllegalArgumentException("Meaningful Exception Message here");
}
I hope it helps! :)
Spring boot allows regular expression checking using #Patter annotation. So just add the following
#Pattern(regexp="[a-zA-Z]")
#NotEmpty(message = "Please provide name")
private String name;

Not able to convert underscore case to camel case with Jackson

I have a DTO class which has a property like:
#JsonIgnoreProperties(ignoreUnknown = true)
public class WPPostResponse {
#JsonProperty("featuredMedia")
Long featured_media;
public Long getFeatured_media() {
return featured_media;
}
public void setFeatured_media(Long featured_media) {
this.featured_media = featured_media;
}
}
The input JSON has the key featured_media. I convert the JSON string to the object and then sends it to the client response as JSON. I want the final response JSON to have featuredMedia as the key. I am however getting null as the value. If I remove the JsonProperty, it gives the value, but the key is having underscore. How to fix this? Thanks.
Always respect the Java naming conventions in your Java code. Use annotations to deal with Json not respecting them.
In this case, use JsonAlias
Annotation that can be used to define one or more alternative names for a property, accepted during deserialization as alternative to the official name
public class WPPostResponse {
#JsonAlias("featured_media")
Long featuredMedia;
public Long getFeaturedMedia() {
return featuredMedia;
}
public void setFeaturedMedia(Long featuredMedia) {
this.featuredMedia = featuredMedia;
}
}
You can use the JsonProperty on setters and getters to have different namings during serialization and deserialization
#JsonIgnoreProperties(ignoreUnknown = true)
public class WPPostResponse {
Long featuredMedia;
#JsonProperty("featuredMedia") // output will be featuredMedia
public Long getFeatured_media() {
return featuredMedia;
}
#JsonProperty("featured_media") // input should be featured_media
public void setFeatured_media(Long featured_media) {
this.featuredMedia = featured_media;
}
}
And also you set access level to #JsonProperty annotation
#JsonProperty(value = "featured_media", access = JsonProperty.Access.WRITE_ONLY)

How to deserialize alternative values for an enum field from JSON request of REST API?

The request model of the REST API has one enum field:
public enum CommentContext {
STUDENT_FEEDBACK,
STUDENT_QUESTION;
}
Now I want to remove the STUDENT_ prefix from the enum value, but without breaking existing callers of the API.
I tried to use #JsonAlias, like this:
public enum CommentContext {
#JsonAlias ("{FEEDBACK, STUDENT_FEEDBACK}")
FEEDBACK,
#JsonAlias ("{QUESTION, STUDENT_QUESTION}")
COMMENT;
}
But the API is failing with 400 Bad Request, when STUDENT_FEEDBACK is passed as the value of that enum field in the request JSON.
Is it possible to deserialize this CommentContext object from JSON for either of the alternative values of this enum field such as FEEDBACK or STUDENT_FEEDBACK?
You can do it using #JsonCreator. Here i gave solution for your problem:
public enum CommentContext {
STUDENT_FEEDBACK,
STUDENT_QUESTION;
#JsonCreator
public static CommentContext setValue(String key){
Optional<CommentContext> commentContext = Arrays.stream(CommentContext.values())
.parallel()
.filter(ct -> ct.toString().equals(key) || ct.toString().substring(8).equals(key))
.findAny();
return commentContext.orElse(null);
}
}
you can also see this link

Dealing with changed ENUM definitions - database

Introduction
The lead architect went and changed the ENUM definition in a spring boot project.
From:
public enum ProcessState{
C("COMPLETE"), P("PARTIAL");
}
To:
public enum ProcessState{
COMPLETE("COMPLETE"), PARTIAL("PARTIAL");
}
What is the proper way to deal with this? Some other Java Spring Boot applications are now breaking. Would there be a way to tell the jackson deserializer to perform some kind of conversion in these situations?
My Current Work-Around
What I did was to run two update statements on the oracle database:
UPDATE store set PAYLOAD = REPLACE(PAYLOAD, '"processState":"P"','"processState":"PARTIAL"') where PAYLOAD like '%"processState":"P"%';
UPDATE store set PAYLOAD = REPLACE(PAYLOAD, '"processState":"C"','"processState":"COMPLETE"') where PAYLOAD like '%"processState":"C"%';
Question
So are there other ways? Could I do it by adding some deserialization/conversion code somewhere for these specific cases? Is there a more elegant way than running a replace SQL statement?
Could I do some kind of hack on a specific java sub-package, and say "use this enum instead of that enum..." or use one of the two? But without affecting the rest of the code?
The error:
java.lang.IllegalArgumentException: No enum constant
Ideally we store value of emum rather than Enum.
So, you should save ENUM values like COMPLETE,PARTIAL
For JSON serialization and de-serialization, use #JsonValue
#JsonValue
public String toValue() {
return value;
}
One additional solution to the others posted:
#JsonCreator
public static ProcessState factory(String inputValue) {
if(inputValue.length() == 1){
for(ProcessState type : ProcessState.values()){
if(inputValue.equals(type.getValue().substring(0,inputValue.length()))){
return type;
}
}
}
return ProcessState .valueOf(inputValue);
}
Implement a JPA converter like this:
#Converter(autoApply = true)
public class ProcessStateConverter
implements AttributeConverter<ProcessState, String> {
private ImmutableBiMap<ProcessState, String> map = ImmutableBiMap.<ProcessState, String>builder()
.put(COMPLETE, "C")
.put(COMPRESSING, "P")
.build();
#Override
public String convertToDatabaseColumn(ProcessState attribute) {
return Optional.ofNullable(map.get(attribute))
.orElseThrow(() -> new RuntimeException("Unknown ProcessState: " + attribute));
}
#Override
public ProcessState convertToEntityAttribute(String dbData) {
return Optional.ofNullable(map.inverse().get(dbData))
.orElseThrow(() -> new RuntimeException("Unknown String: " + dbData));
}
}
Remember to treat your Enum like a simple column and not #Enumerated i.e.
#Entity
public class MyEntity {
#Column //no #Enumerated
private ProcessState processState;
//...
}
The drawback is that you need to maintain the converter each time something changes. So better create a unit test to check if everything is correctly mapped.

Jackson #JsonIgnore property is null

I want convert json string to one object.
The json looks like this:
{"receive":1413342268310}
And the object is like:
public class PositionBean {
private Long id;
private Date receive;
public void setReceive (Date receive) {
this.receive = receive;
}
public void setReceive (Long receive) {
this.receive = new Date (receive);
}
public Long getReceive () {
return receive.getTime ();
}
}
All the set and get methods I have to use in other class, so I can't delete one method.
When I invoke
objectMapper.readValue(str, PositionBean.class);
It prompt exception, the jackon don't know which method set, so I use #JsonIgnore, but I found the receive is null.
You can use annotation #JsonSetter to specify which method should be used as setter.
Example:
public class PositionBean {
private Long id;
private Date receive;
public void setReceive (Date receive) {
this.receive = receive;
}
#JsonSetter
public void setReceive (Long receive) {
this.receive = new Date (receive);
}
public Long getReceive () {
return receive.getTime ();
}
}
When you mark setter (e.g. setXXX) with #JsonIgnore it means that property XXX will be ignored.
From documentation:
For example, a "getter" method that would otherwise denote a property
(like, say, "getValue" to suggest property "value") to serialize,
would be ignored and no such property would be output unless another
annotation defines alternative method to use.
You can also use
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
This will not throw any mapping exception even if u dont have an appropriate field in the mapping class corresponding to a JSON field. Once configured u can use ur code for further processing.
objectMapper.readValue (str, PositionBean.class);

Categories