How to hide fields with a condition in RESTful WS? - java

I have a class called Report that I need to share using RESTful WS.
once in full with all its attributes
once in only a reduced version
Normally I'd use something like #XmlTransient to hide the fields, but this would prevent the full version from working.
Is there any way to set a condition or to kind of pre-filter fields just before the output so that it doesn't affect other uses of the same class?
My Report class looks like this:
public class Report {
private String reportId;
private String title;
private String content;
private Date created;
private Date modified;
...
}
The RESTful sharing for the full Report looks like this:
#GET
#Path("/{reportId}")
public Report getReport(#PathParam("reportId") String reportId) {
return Mock.getReport(reportId);
}
The full output I need looks like this:
{
"reportId": "d83badf3",
"title": "The tales of lamaru",
"content": "When once the great Imgur started his journey...",
"created": 1519672434866,
"modified": 1519672434866
}
The short output I need should look like this:
{
"reportId": "d83badf3",
"title": "The tales of lamaru"
}
What is necessary to achieve this?

Why don't you use Inheritance?
Parent
public class Report {
private String reportId;
private String title;
}
Child
public class FullReport extends Report{
private String content;
private Date createdp;
private Date modified;
}
When you need full report set return type as FullReport otherwise Report

Jackson has two different annotations to use when you want to exclude some class members from the JSON serialization and deserialization processes. These two annotations are #JsonIgnore and #JsonIgnoreProperties.
#JsonIgnoreProperties is an annotation at the class level and it expects that the properties to be excluded would be explicitly indicated in the form of a list of strings.
#JsonIgnore instead is a member-level or method-level annotation, which expects that the properties to be excluded are marked one by one.
try this.
public class Report {
private String reportId;
private String title;
#JsonIgnore
private String content;
#JsonIgnore
private Date created;
#JsonIgnore
private Date modified;
...
}

Related

QUARKUS - MicroProfile REST Client: add a custom, not mapped field

I am following this article https://quarkus.io/guides/rest-client to build a REST Client to parse the output from the restcountries.eu service.
Here the class holding the model:
public class Country {
public String name;
public String alpha2Code;
public String capital;
public List<Currency> currencies;
public static class Currency {
public String code;
public String name;
public String symbol;
}
}
Now, suppose I would like to add a custom fields such as timestamp, to record the instant when this object has been created. I imagine, I would go ahead and add another field like below:
public class Country {
public String name;
public String alpha2Code;
public String capital;
public List<Currency> currencies;
public Instant timestamp; //<--------- added attribute
[....]
My question is: how do I tell the client to populate that field? Normally, I would have done it in the constructor. However, I could not find docs that explain this part.
Thanks for your help
Simone
You can actually do this in the default constructor. Frameworks like JSONB or Jackson expect POJOs to have a default constructor. They will call it when they create an instance of Country.
Use the #JsonbTransient or #JsonIgnore annotations to mark that attribute of your POJO as ignorable in order to avoid the unmarshaller complaining about attributes that cannot be found in the response.
#Data
public class Country {
private String name;
private String alpha2Code;
private String capital;
private List<Currency> currencies;
#JsonbTransient // if you're using JSONB (default in Quarkus)
#JsonIgnore // if you're using Jackson
private Instant timestamp;
public Country() {
this.timestamp = Instant.now();
}
PS The #Data annotation is something you should consider using. Encapsulation is not a bad thing but creating getters/setters is tedious. But Project Lombok certainly helps here.

How can parse this data when one field change of name?

I am trying to make several POJOs using Json.fromJson, to parse a String json to a POJO.
To make this I have the following class:
public class Queue {
#SerializedName("reference")
#Expose
private String reference;
#SerializedName("type")
#Expose
private QueuesTypes type;
#SerializedName("desc")
#Expose
private String desc;
#SerializedName("alias")
#Expose
private String alias;
private QueueObjects queueObjects;
}
As you can see all objects have their notations less the last because in this case is not the same.
Sometimes that label should be calls, or whatsapps or tweets, according to the information.
And this Queue Object that can has different attributes to the last object because is merged in the response like this:
{"success":true,"data":[
{"reference":"","type":"","desc":"","alias":"","calls":[{fromPhone:'', toPhone:''}]},
{"reference":"","type":"","desc":"","alias":"","whatsapps": [message:'']},
{"reference":"","type":"","desc":"","alias":"","calls":[fromPhone:'', toPhone:'']},
{"reference":"","type":"","desc":"","alias":"","calls":[fromPhone:'', toPhone:'']},
{"reference":"","type":"","desc":"","alias":"","whatsapps": [message:''],}
{"reference":"","type":"","desc":"","alias":"","fax": [fromFax:'', toFax:'', message:'']}]
So this is a:
public class SocketQueueResponse {
#SerializedName("success")
#Expose
private boolean success;
#SerializedName("data")
#Expose
private List<Queue> listQueue;
}
The problem is how can put several attributes with its several kind ob objects according to the response in QueueClass.
Now I have
public interface QueueObjects {
}
And another class according to the response, but the problem is how can set the notation to QueueObjects.
Thanks.

spring boot dynamic object modelling

I am creating a RESTful API using spring boot. I have the requirement where I need to make a request to the resource
/user/notification
Notification resource will accept values in bodyrequest and send the notification to users.
#ResponseBody
#RequestMapping(value = "/user/notification", method = RequestMethod.POST)
public NotificationResponse sendNotification(#Valid #RequestBody NotificationRequest notificationRequest){
// here is code where I need to build right
// object of type text/file/link/map (please read full question below)
notificationService.send(notificationRequest.getUsername(), object);
}
It accepts: username and data for notification. Here is NotificationRequest class:
public class NotificationRequest {
#NotEmpty
private String username;
#NotEmpty
private String type;
private String title;
#NotEmpty
private String content;
private String url;
private String longitude;
private String latitude;
private String file_url;
//getters and setters
}
I have 4 types of notifications ie. text, link, map and file. And their attributes are these.
text
- type
- title
- content
link
- type
- title
- content
- url
map
- type
- title
- longitude
- latitude
file
- type
- title
- content
- file_url
I created 4 classes for these so I can create the right object, As you can see type and title are common attributes so I used inheritance.
public class NotificationBase {
private String type;
private String title;
//getters and setters here
}
And extended other 4 classes like this.
public class TextNotification extends NotificationBase {
private String content;
//getters and setters here
}
My question is, How I create my classes so that
if someone wants to send text notification I would able to get an object of TextNotification and if someone wants to send file notification that I would able to create FileNotification object?
Note: Please note I do not want to use gJson or Jackson to create JSON objects in this case.
Please do let me know if I need to add any more information here.
You can use jackson annotation for subtyping. In your case, you have the field type that will represent what is the type of the object that you expect.
So it's better to have an abstract NotificationRequest class only with needed fields:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
#JsonSubTypes({
#JsonSubTypes.Type(value = TextNotificationRequest.class, name = "text"),
#JsonSubTypes.Type(value = LinkNotificationRequest.class, name = "link")})
public abstract class NotificationRequest {
#NotEmpty
private String username;
#NotEmpty
private String type;
private String title;
public abstract NotificationResponse buildNotificationResponse();
}
And the implementation of each type that you need.
public class TextNotificationRequest extends NotificationRequest {
public String content;
public NotificationResponse buildNotificationResponse{
return new TextNotificationResponse(content);
}
}
Now you can just do something like this
#ResponseBody
#RequestMapping(value = "/user/notification", method = RequestMethod.POST)
public NotificationResponse sendNotification(#Valid #RequestBody NotificationRequest notificationRequest){
NotificationResponse response = notificationRequest.buildNotificationResponse();
notificationService.send(notificationRequest.getUsername(), response);
}
Here is some explanation. By setting these annotations your are saying to jackson which class to instantiate based on the value of type field. If type == "text" jackson will instantiate TextNotificationRequest and so on. And now you can use the power of polymorphism to create NotificationResponse and avoid if-else statements.
Also jackson allow subtyping based not only on the field value. You can read in more detailed info here #JsonTypeInfo

Jackson custom deserializer of multiple properties into value object class

Let's say I have following flat JSON structure:
{
"name": "name",
"validFrom": "2018-01-09",
"validTo": "2018-01-10",
}
and MyPojo class:
public class MyPojo {
private String name;
#JsonUnwrapped
private Validity validity;
}
and Validity class:
public class Validity {
private LocalDate validFrom;
private LocalDate validTo;
}
I created custom unwrapping serializer and it works fine.
I would like to deserialize JSON above into MyPojo class which includes Validity value object.
How should custom deserializer for Validity be implemented?
#JsonProperty does not work as I want to use 2 Json properties for Validity construction
I would recommend a constructor in this case, a lot simpler than a custom deserializer, something like:
#JsonCreator
public MyPojo(#JsonProperty("name") String name,
#JsonProperty("validFrom") String validFrom,
#JsonProperty("validTo") String validTo) {
this.name = name;
this.validity = new Validity(validFrom, validTo);
}
It's implied that LocalDate is parsed from String above but you may have Jackson parse them.
You may skip annotations above if you use Java 8 with parameter names module
That will require an extra annotation on validity, see open Jackson issue here
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
#JsonUnwrapped
private Validity validity;

How to apply Single annotation on multiple variables?

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;

Categories