Spring and Jackson Json: serialising two different sets of fields - java

I've a Classified interface, annotated with #JsonAutoDetect with Visibility.NONE, so I can pick individual getters to be serialized with the #JsonSerialize annotation
#JsonAutoDetect(getterVisibility = Visibility.NONE)
public interface Classified {
#JsonSerialize
String getModel();
Until here there is no problem, and when I return Classified with #ResponseBody annotation, from my #Controller, it works returning the expected JSON:
#RequestMapping(value = "/classified/{idClassified}", method = RequestMethod.GET)
#ResponseBody
public final Classified getClassified(#PathVariable final int idClassified) {
However when I return a List of Classifieds, I would like to return a smaller set of getters, while with the following signature, obviously it returns all marked getters:
#RequestMapping(value = "/classified", method = RequestMethod.GET)
#ResponseBody
public final List<Classified> searchClassified(#RequestParam final int idBrand,
#RequestParam final String priceMax, #RequestParam final int page) {
I don't know how to return a smaller subset of Classified getters in each item of the list.

Check out "filtering properties", which lists multiple ways to change what gets serialized. I would guess that Json Views might be the easiest one; could use one smaller view, and then default "all" mode when no view is defined (default is then to serialize all properties).

"return a smaller set of getters"
If you mean reduce the number of items in the list, change the business logic in the controller's searchClassified method.
If you mean reduce the number of available public getter methods on each item, you could create a interface that only implements a subset of the original items getters, and return a list of them instead.

Related

API-method can return different types, how do i define the return type?

I have the following API method:
#GetMapping
public ResponseEntity<List<Project>> getProjects(#RequestParam(required = false) String userName, #RequestParam(required = false) boolean additionalInfo) {
return ResponseEntity.ok(projectService.getProjects(userName, additionalInfo));
}
It currently returns ResponseEntity<List<Project>>. But if the optional paramater additionalInfo is true, i would like to return ResponseEntity<List<ProjectAdditionalInfoDTO>>. How do i define the return type to indicate that both of them can be returned? Of course i could use ResponseEntity<?>, but that would be ugly.
I'd keep things simple and just split things up into two separate controller methods... e.g.
#GetMapping
public ResponseEntity<List<Project>> getProjects(#RequestParam(required=false) String userName) {
//...
}
#GetMapping
public ResponseEntity<List<ProjectAdditionalInfoDTO>> getProjectsWithAdditionalInfos(#RequestParam(required=false) String userName, #RequestParam(required=true) boolean additionalInfo) {
//...
}
If they both have the same interface (let's say BaseProject) you can make it return the following:
ResponseEntity<List<? extends BaseProject>>
There is no possibility to show that 2 options are possible in the return type via the method signature.
As far as I can understand ProjectAdditionalInfoDTO should inherit Project. That means that if you add inheritance there, then you can return the parent class since ProjectAdditionalInfoDTO would always be Project. If not, then since they are different resources you should have different endpoints to access them.

Diff two classes fields names and structure

My task
Force two classes to have the same (or similar) field names (and their types)
Description
I have an entity and Data Transfer Objects (DTO).
How do I force that if someone adds / removes / changes a field in entity, a test will fail, so DTO class matches the entity class by fields names and if possible by fields structure.
class City {
private String name;
private CityDetails cityDetails;
private Mayor mayor;
}
class Mayor {
private String name;
private LocalDate electionFrom;
private LocalDate electionTo;
}
class CityDto {
private String name;
private CityDetailsDto cityDetails;
private MayorDto mayor;
}
class MayorDto {
private String name;
// The client of the end-point of such DTO does not care about the mayor election.
}
CityDetails and CityDetailsDto can be different or not necessary. I would like to have their diff.
Approaches
Using so called diff tool
Let's imagine a diff tool which has input parameters - two objects and output parameter - Map, where Diff is a structure oldValue, newValue. This tool returns difference between input arguments.
public Map<String, Diff> diff(final Object first, final Object second) {
// This is implemented.
return innerDiff(first, second, otherParameters); //
}
public class Diff {
private String oldValue;
private String newValue;
// getters, setters, constructor.
}
// To achieve this, we used Guava Plain map. It works well!
How do I achieve the same for classes. I want to diff two classes and have their fields as difference.
public Map<String, FieldDiff> diff(Class<?> type1, Class<?> type2) {
// How?
}
One idea is to use reflection and iterate though all fields of the class.
How about using toString and compare?
As an alternative, we define toString() methods in both and then compare.
How to do that if some fields are missing?
Serialize into JSON
Similar to the previous one, but serializing both objects into JSON and then compare their classes by json field names. However, if our entity have already annotation DoNotSerializeNulls with is equal to #JsonInclude(JsonInclude.Include.NON_NULL) #JsonIgnoreProperties(ignoreUnknown = true), then how to do that?
References
https://www.baeldung.com/entity-to-and-from-dto-for-a-java-spring-application

Spring MVC controller parameters binding: replace parameters with POJO

I have a simple controller method with more than 7 parameters and would like to refactor it using a model object instead, i.e. to extract a parameter object:
#RequestMapping(value="/{one}")
public String controllerMethod(#PathVariable(value="one") String one, #RequestParam String two, ... #RequestBody body) {
...
}
I tried to extract an object with setters and getters and pathVariable and requestParameters are mapped by name. However I have troubles making the same for #RequestBody, it doesn't work for me even if I put #RequestBody into setter...
public class Parameters {
private String one; // pathVariable
private String two; // requestParameter
private String body;// requestBody - always NULL!
// other fields definition
public setBody(#RequestBody String body) {this.body = body}
//other setters/getters
}
How to keep #RequestBody parameter in extracted POJO?
Another question is how to control name of parameters, i.e. if
parameter name differs from the field name in POJO, is there any
annotation? This one doesn't work:
public void setOne(#RequestParameter(value="o") String one) {this.one = one}
How to mark the fields as required or give a default value like in the #RequestParameter annotation?
For the (1) I would simply keep #RequestBody as a separate parameter though I don't like it much.
Ok, looks like the only way of doing (2) and (3) is through customizing data binding: the similar question
Feel free to post another easy solution if you know about it.

How to use enum constant in request mapping annotation?

This is as enum of constants.
public enum LoginRequestMappingConstants
{
LOGIN("/login"),
LOGOUT("/logout"),
ADMINISTRATION("/Administration");
private LoginRequestMappingConstants(String requestMapping) {
this.requestMapping = requestMapping;
}
private String requestMapping;
public String getRequestMapping() {
return requestMapping;
}
}
In request mapping annotation I wanted to use the enum of constant.
#RequestMapping(value = LoginRequestMappingConstants.LOGIN.getRequestMapping(), method = RequestMethod.GET)
During compile time only I am getting this error.
The value for annotation attribute RequestMapping.value must be a constant expression. What is meaning of this error ?
What is the correct way to create constants for RequestMapping annotation?
You can't use an enum; you can only use a String. It frequently doesn't make sense to put those paths in a separate place from the controller, but if you do need to, use ordinary constants:
public interface LoginRequestMappings {
String LOGIN = "/login";
String LOGOUT = "/logout";
}
#RequestMapping(LoginRequestMappings.LOGIN)

Handling Multiple Query Parameters in Jersey

In the web service I'm working on, I need to implement a URI with query parameters which look like /stats?store=A&store=B&item=C&item=D
To break it down, I need to be able to use query parameters to specify data from multiple/all stores and data for multiple/all items from those stores. So far I have been able to implement one query argument just fine in order to pull item data, but I'm lost as far as to how to implement more queries, and can't seem to find the resources I had seen before which deal with this implementation.
What I have so far in my method is along the lines of
#GET
#Path("stats")
public String methodImCalling(#DefaultValue("All") #QueryParam(value = "item") final String item)
{
/**Run data using item as variable**/
return someStringOfData
}
which works well for one item, and will return all data if I don't type the parameter in the URI. However, I am unsure how to handle any more parameters than this.
Update:
I have figured out how to use 2 different parameters by simply adding a second argument to the method like so:
public String methodImCalling(#DefaultValue("All") #QueryParam(value = "store") final String store,
#DefaultValue("All") #QueryParam(value = "item") final String item)
The question remains of how to implement multiple values of the same parameter.
If you change the type of your item method parameter from String to a collection such as List<String>, you should get a collection that holds all the values you are looking for.
#GET
#Path("/foo")
#Produces("text/plain")
public String methodImCalling(#DefaultValue("All")
#QueryParam(value = "item")
final List<String> item) {
return "values are " + item;
}
The JAX-RS specification (section 3.2) says the following regarding the #QueryParam annotation:
The following types are supported:
Primitive Types
Types that have a constructor that accepts a single String argument.
Types that have a static method named valueOf with a single String argument.
List<T>, Set<T>, or SortedSet<T> where T satisfies 2 or 3 above.
List<String> items=ui.getQueryParameters().get("item");
where ui is declared as a member in the rest resource like so :
#Context UriInfo ui;
the downside is that it doesn't appear in the methods arguments at all.
Some libs like axios js use the square brackets notation when sending a multi-value param request: /stats?store[]=A&store[]=B&item[]=C&item[]=D
To handle all cases (with or without square brackets) you can add another param like this:
public String methodImCalling(
#QueryParam(value = "store") final List<String> store,
#QueryParam(value = "store[]") final List<String> storeWithBrackets,
#QueryParam(value = "item") final List<String> item,
#QueryParam(value = "item[]") final List<String> itemWithBrackets) {
...
}
Inspecting each of the arguments checking for null.

Categories