jackson generating an unexpected "new" field - java

I'm using spring rest and jackson to generate json. For the class country
public class Country extends AbstractPersistable<Long> {
private String name;
private String code2;
private String code3;
public Country() {
}
public Country(String name, String code2, String code3) {
...
}
...
}
I get, for example,
{
"id" : 1,
"name" : "Afghanistan",
"code2" : "AF",
"code3" : "AFG",
**"new" : false**
}
For some classes I get an unexpected "new" field always set to false. I suspect it has something to do with the parametrized constructor, but it's just a guess. Ideas?

The class AbstractPersistable has a public method called isNew specified by the interface Persistable (the doc here).
You will have to ignore such property if you don't want it in your JSON, for example, using the annotation JsonIgnoreProperties in your class.

Related

Spring Java POST endpoint that can have a field be different data types

I'm working on a Java Spring Boot HTTP Service application. I currently have a POST endpoint that I have defined inside of a #RestController. This controller, called processRequest takes an object called Info with the #RequestBody annotation.
Right now, I have it setup where a user can send JSON based on the Info class that I defined like this:
//Sample JSON Payload
{
"name": "Bob",
"age": 26,
"hobby": biking
}
//Sample Object
#RequiredArgsConstructor
public class Info {
public final String name;
public final int age;
public final String hobby
}
What I want to do know is respond to the situation where one of the fields is sent as a different datatype. For example:
//JSON payload with different datatype for a field
{
"name": "Bob",
age: 26,
"hobby": ["biking", "hiking"] //This is supposed to be a string but it's an array.
}
Is it possible to keep the endpoint properties the same but handle different data types? Maybe I can create another class where the fields are different and spring will automatically create the one that matches the input? I'm curious for what the best approach to this problem would be.
In this particular example, where the hobby could either be a single value or multiple values, I would rely on the Jackson ACCEPT_SINGLE_VALUE_AS_ARRAY deserialization feature.
This can be configured application-wide within application.properties:
spring.jackson.deserialization.accept-single-value-as-array=true
Or this can be enabled for a specific field:
#RequiredArgsConstructor
public class Info {
public final String name;
public final int age;
#JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
public final List<String> hobby
}
For more complex cases, Jackson recommends that you write a wrapper with a specific type field to provide a hint of which type it should deserialize. For example:
public class InfoWrapper {
private String type;
#JsonTypeInfo(use = Id.NAME, property = "type", include = As.EXTERNAL_PROPERTY)
#JsonSubTypes(value = {
#JsonSubTypes.Type(value = PersonInfo.class, name = "person")
})
private Info info;
}
public interface Info {}
#RequiredArgsConstructor
public class PersonInfo implements Info {
public final String name;
public final int age;
public final String hobby
}
So, if you want to send a JSON containing PersonInfo, you can use the following JSON:
{
"type": "person",
"info": {
"name": "Bob",
"age": 26,
"hobby": "biking"
}
}
If you need more advanced control over what you want to do, you can write a custom deserializer and apply it with the #JsonDeserialize annotation.
You can use JsonNode for the field which changes. As below:
public class Info {
public String name;
public int age;
public JsonNode hobby;
#Schema(description = "")
#Valid
#JsonIgnore
public List<String> getHobbies() {
// if hobby is array create Java object and return
// else if hobby is String create List, add the string to it and return it.
// or some other solution of your choice and requirement
}
}

How do I attach the enum value to an object, instead of the enum name?

So my title is probably not the best one, but I think I can better show it in code what I want to achieve. I'm doing things with Spring Boot, JPA and React.
So let's say I have this enum:
public enum MyEnum {
FIRST_ENUM("First"),
SECOND_ENUM("Second")
}
And there's a class that contains one of the enums:
public class MyClass {
private int id;
private MyEnum myEnum;
}
When I convert an instance of this class to JSON (so I can pass it to React as JSON), this is what I get:
{
"id": 1,
"myEnum": "FIRST_ENUM"
}
But instead I want it to use the value instead (I know I can manually create JSONObject instances, but is there a way to automatically use the value instead?):
{
"id": 1,
"myEnum": "First"
}
public enum MyEnum {
FIRST_ENUM("First"),
SECOND_ENUM("Second");
#JsonValue
private final String name;
MyEnum(String name) {
this.name = name;
}
#JsonCreator
public static MyEnum fromString(String string) {
...//find enum constant by name
}
}
Another option is to use #JsonProperty for the enum values if the purpose of the custom name is to represent the enum's value in JSON:
public enum MyEnum {
#JsonProperty("First") FIRST_ENUM,
#JsonProperty("Second") SECOND_ENUM;
}

#JsonValue and #ApiModelProperty conflicts

in my project I am using Swagger to document REST API. I have simple value object which I want to document.
public class MyClass {
#JsonValue
private String myField;
public String getMyField() {
return myField;
}
}
Unfortunately, when I am adding swagger annotations and then see created documentation, there is no information about this VO.
Class with swagger annotations below:
#ApiModel(value = "MyClass ", description = "represents my class")
public class MyClass {
#JsonValue
#ApiModelProperty(value = "name", dataType = "String", example = "my field")
private String myField;
public String getMyField() {
return myField;
}
}
Wanted to check this issue I temporally removed #JsonValue annotation and documentation was created properly, annotation #ApiModelProperty worked.
I cannot remove #JsonValue annotation permanently.
Does anybody know the solution how can I force those two tools to cooperate?

Java - How to convert a String into specific enum implementing an interface

Consider below inner Enum implementing an interface:
public interface NotificationTypes {
public enum CONTACT_LIST implements NotificationTypes{
ADDED("CONTACT_LIST-ADDED"),
REMOVED("CONTACT_LIST-REMOVED");
public enum INVITATION implements NotificationTypes{
ACCEPTED("CONTACT_LIST-INVITATION-ACCEPTED"),
REJECTED("CONTACT_LIST-INVITATION-REJECTED");
String name = "";
private INVITATION(String name){
this.name = name;
}
public String getName(){
return name;
}
};
String name = "";
private CONTACT_LIST(String name){
this.name = name;
}
public String getName(){
return name;
}
}
public String getName();
}
Now consider that data in database/mongodb is stored in the form of String for NotificationTypes in a table/document.
{
"_id" : ObjectId("59882ba49e5d82c72ba44fde"),
"template" : "Contact list Invitation accepted by your friend",
"type" : "CONTACT_LIST-INVITATION-ACCEPTED"
}
So my question is: How to convert that string back into specific enum at runtime without knowing exactly the name of enum to be mapped?
Domain class is looks like this:
#Document(collection = CollectionNames.XXX_TEMPLATE)
public class XXXTemplate {
private NotificationTypes type;
//Other parameters, getters & setters + Constructor
}
I'd build a Map<String, NotificationTypes> and populate that with all instances you have. You can then look up from that map.
I don't think the compiler can help you a lot with keeping that in sync, other than that you can loop over EnumType.values() (but you have to remember to do it for all of your enum types).
How to convert that string back into specific enum at runtime without knowing exactly the name of enum to be mapped?
Via Enum.valueOf().
Basically just building on #Thilo's answer, but perhaps a more 'Springified' way if it's something that you'd want - you could define a #Bean in your config that contains all your enum values, like:
#Configuration
public class Config {
#Bean
public List<NotificationTypes> notificationTypes() {
List<NotificationTypes> notificationTypes = new ArrayList<>();
notificationTypes.addAll(Arrays.asList(NotificationTypes.CONTACT_LIST.values()));
notificationTypes.addAll(Arrays.asList(NotificationTypes.CONTACT_LIST.INVITATION.values()));
return notificationTypes;
}
}
And then #Autowire this #Bean into a parser to do the actual matching of String to enum, something like:
#Component
public class NotificationTypeParser {
#Autowired
private List<NotificationTypes> notificationTypes;
public NotificationTypes parseNotificationType(String type) {
for (NotificationTypes notificationType : notificationTypes) {
if (notificationType.getName().equals(type)) {
return notificationType;
}
}
return null;
}
}
Obviously you probably want something better than just returning null if the enum isn't found, and you could potentially do something smarter in the #Bean definition to validate that the enums all have different names, etc. Or, conceivably, use reflection in there to find all the implementations of NotificationTypes.
I'm not sure that this really gives you any additional benefits over just storing all the possible values in a Map, but, as I say, I suppose it's a bit Spring-ier.

Validation (#Valid) of two-dimensional list (List<List<X>>)

I have a class X:
class X {
#NotNull
#NotEmpty
private final String name;
#JsonValue
public String name() {
return name;
}
#JsonCreator
public X(final String name) {
this.name = name;
}
}
In another class, I have the following:
class Input {
#Valid
#JsonProperty("data")
private List<List<X>> data;
}
I am parsing a JSON input file with Jersey and validate with Hibernate Validator. The validation does not seem to be executed - an empty string is accepted.
"data": [
[
""
],
[
"name",
"location"
]
]
You can write your own annotiation to check two dimensional list on top of the project:
https://github.com/jirutka/validator-collection
It shows how to validate elements in a collection. It could be helpful in your case because neither jsr303 nor jsr349 gives you possibilty to validate out of the box
This usecase is not supported by Bean Validation. The #Valid annotation on List<List<X>> data; will instruct the validator to iterate each List<X> element and check for its validity. For this the class List will be checked for bean validation constraints, which don't exist (your constraint is on X). #Valid is not recursive in any form.
A workaround would be to use your own wrapper class to host the list of X instances. In this wrapper class you then can annotate the wrapped list with #Valid.

Categories