I have rest server with spring.
There is a lot of requests where one of the params is fields fields is the set of fields that server should return in response. like: /?fields=[id,name] and server should return JSON object with both fields
I would like to know what is the best practice for generating such response.
We do it like this:
private Map<String, Object> processBook(BookEntity book, Set<String> fields, String locale){
Map<String, Object> map = new HashMap<String, Object>();
//..
if(fields.contains(ID)){
map.put(ID, book.getId());
}
if(fields.contains(ISBN)){
map.put(ISBN, book.getIsbn());
}
if(fields.contains(DESCRIPTION)){
if(locale.equals(UserLocale.UK)) map.put(DESCRIPTION, book.getDescriptionUa());
else if(locale.equals(UserLocale.RU)) map.put(DESCRIPTION, book.getDescriptionRu());
else map.put(DESCRIPTION, book.getDescriptionEn());
}
//..
return map;
}
Maybe there is much better alternative?
Note that in your case you obtain all data from DB - fully filled BookEntity object, and then show only requested fields.
In my opinion it'd be "much better alternative" to delegate field list to appropriate downstream integration call and get BookEntity object only with necessary fields. Then mentioned above method will reduce to just one line, your DB responses will be more lightweight, so it will bring simplicity and optimization gain to your system.
Any adequate DB provides such functionality: SQL or NoSQL, etc.
P.S. Plus standard approach of Object to JSON mapping such as Jackson or GSON at top level.
Instead of having a Map, you could have and object with the attributes you need and set them, instead of adding to map.Then you can use Google's Gson to transform your object into a Json object.Take a look at this quick tutorial.
One approach is to have an asMap function.
Map<String, Object> map = book.asMap();
map.keySet().retainAll(fields);
Related
I have a Request object with field request_type and number of other fields. request_type can be 'additionRequest' , 'deletionRequest' 'informationRequest'.
Based on request_type other fields in Request object are processed differently.
My simple minded approach is
if additionRequest
algorithm1
else if deletionRequest
algorithm2
else if deletionRequest
algorithm3
end
How I can avoid these if statements and still apply proper algorithm?
If you want to avoid conditional statements then you can leverage object oriented features such as:
Map<String, Function<Request, Result>> parsers = new HashMap<>();
parsers.put("additionRequest", request -> {
// parse and generate Result here
return result;
});
Result result = parsers.get(request.request_type).apply(request);
Seems to me that perhaps a Command Pattern could come in handy here. If you make an object structure of these commands and encapsulate the algorithm that you want to execute within the object, then you can construct the specific sub-objects and later on use "execute" method of the command to invoke the algorithm. Just make sure you are using polymorphic references:
if additionRequest
algorithm1
else if deletionRequest
algorithm2
else if deletionRequest
algorithm3
end
will become
void theRequestExecutor(Reqest req) {
req.executeAlgorithm()
}
https://en.wikipedia.org/wiki/Command_pattern
Use HashMap<RequestType, RequestHandler> for this case. RequestHandler can be an interface which will be implemented for each situation you want to handle.
Hope this help.
You can create Map of key:String, value :Function of RequestType ReturnType. Depending on type of request it will call corresponding function.
Example:
Map<String, Function<RequestType, ResponseType> requestProcessors = new HashMap<>;
requestProcessors.add("additionRequest", this::methodToHandleAddRequest);
requestProcessors.add("deletionRequest", this::methodToHandleDeleteRequest);
Inside request handler do
return this.requestProcessors.get(request.request_type).apply(request);
Note you may have to create response interface if different responses are different. Different response type will inherit from response interface
The object-oriented solution is always to include the logic with the data. In this case include whatever you want a request to do in the request object itself, instead of (presumably) passing out pure data.
Something like
public interface Request {
Response apply();
}
Anything else, like creating a map of functions, creating a thin abstraction layer or applying some pattern is a workaround. These are suitable only if the first solution can not be applied. This might be the case if the Request objects are not under your control, for example generated automatically or third party classes.
What use is Java 6 interface MultivaluedMap?
The interface does not belong to "Java", meaning that the interface is not a part of the core libraries. It is a part of the javax.ws.rs hierarchy which is part of the JAX-RS specification. It is used by frameworks implementing the specification such as Jersey. It is used whenever maps should refer to not only a single value but to any number of values. An example for the use would be for example the storage of a request header where you one might want to add several values per key. Or even no keys in some cases where it is easier to handle an empty list compared to a null value.
Take this HTTP-header for example:
Accept-Encoding: compress;q=0.5, gzip;q=1.0
You would model this by
MultivaluedMap<String, String> map = ...
map.add("Accept-Encoding", "compress;q=0.5");
map.add("Accept-Encoding", "gzip;q=1.0");
internally in Jersey. This type of multiple value storage is a common problem in Java that is addressed by other implementors of maps such as Guava.
This is basically what the javadoc says:
A map of key-values pairs. Each key can have zero or more values.
Its a map of key-values pairs. Each key can have zero or multiple values
public interface MultivaluedMap<K,V> extends java.util.Map<K,java.util.List<V>>
A good use of a MultivaluedMap is with UriInfo. If you are writing a REST endpoint that takes in several QueryParams, you can use UriInfo to get all of the params and extract them using the getQuery() call. For example:
public void get(#Context UriInfo ui) {
MultivaluedMap params = ui.getRequestUri().getQuery();
// now do what you want with your params
}
The MultivaluedMap is helpful because you could have parameters with multiple values. For example if it was a customer database and you wanted to get several customers, your map would have the key of "customerID" and several values linked to it.
MultiValuedMap is a part of javax.ws.rs.core package, not Core Java. It is mainly used for storing Headers values in requests
private MediaType getMediaType(Class entityClass, Type entityType, MultivaluedMap<String, Object> headers) {
final Object mediaTypeHeader = headers.getFirst("Content-Type");
....
}
Also, it is quite useful with UriInfo
private String getJsonpFunctionName(){
UriInfo uriInfo = getUriInfo();
if (uriInfo == null) {
return null;
}
MultivaluedMap<String, String> queryParameters = uriInfo.getQueryParameters();
if (queryParameters == null) {
return null;
}
return queryParameters.getFirst("jsonp");
}
In Spring MVC we have to use org.springframework.ui.Model instance to pass model to a view. It is not strongly-typed and we have to dynamically build the object like this:
model.addAttribute("departmentID", departmentID);
model.addAttribute("departmentName", departmentName);
model.addAttribute("employees", employees);
However, I came from ASP.NET MVC, where I passed strongly-typed objects to a view, and I had ViewDepartment class which had departmentID, departmentName and employees fields, and I simply passed instance to a view. Here it doesn't work, but I still need to use ViewDepartment class, because I occasionally send it as response to AJAX-requests.
So, to get this working in Spring MVC, I need to translate ViewDepartment object to instance of org.springframework.ui.Model, one way is to build org.springframework.ui.Model from HashMap:
Model.addAllAttributes(Map<String,?> attributes)
The question is, how to build Map<String,?> attributes from instance of ViewDepartment class? Creating HashMap object and manually setting each attribute from each property is not option, because it`s not DRY. I need some way to do this with any class, because I have other model classes in other controllers.
Or, may be, someone can tell another solution to this task, related to Spring MVC specifically.
you could use reflection to get a map of all fields and their values. Be aware, that this gets complicated, if you have nested structures, but with the given example it should work,
public Map<String, Object> toMap( Object object ) throws Exception
{
Map<String, Object> map = new LinkedHashMap<>();
for ( Field field : object.getClass().getDeclaredFields() )
{
field.setAccessible( true );
map.put( field.getName(), field.get( object ) );
}
return map;
}
I have a spring MVC application(RestFul), The controller has a method/API which returns
Map<Long, List<Long>>.
I need to call the above API in another web application. To do this I have written a client program which will internally call
the API and return the data.
But instead of sending
Map<Long, List<Long>>
it always sends data in
Map<String, List<String>>.
Can't I send directly
Map<Long, List<Long>>
If I create a BO/TO(Java Bean) and which has a property of type Map>
then I am able to get the data in proper format
Below is the code snippet.
public Map<Long, List<Long>> get(Long sourceId){
Map<Long, List<Long>> map = null;
// codes to perform operation and putting data into map.
return map;
}
Can you please suggest what is the issue ?
Anything sent over the wire is a String ... but your code picking up the response should convert it to long. Well it would if it were a spring-mvc controller with the correct method signature.
Are you using Javascript? Try to use the javascript parseFloat method on your JSON data
I am currently using the Jackson library to unmarshal json to pojos, using annotations.
The json tree that I would like to unmarshal is as follows:
{
"key1":"value1",
"key2":"value2",
"key3":{
"key31":{
"key311":"value311",
"key312":"value312",
"key313":"value313"
},
"key32":"value32"
},
"key4":"value4",
"key5":"value5",
"key6":{
"key61":"value61"
}
}
I don't know the json structure in advance and would like to completely flatten it to a Map which content would be equivalent to:
Map<String, Object> outputMap = new HashMap<String, Object>();
outputMap.put("key1", "value1");
outputMap.put("key2", "value2");
outputMap.put("key311", "value311");
outputMap.put("key312", "value312");
outputMap.put("key313", "value313");
outputMap.put("key32", "value32");
outputMap.put("key4", "value4");
outputMap.put("key5", "value5");
outputMap.put("key61", "value61");
(note that keys "key3", "key31" and "key6" should be ignored)
Using the annotation #JsonAnySetter, I can create a function to populate the map with all top level atoms, but the method will also catch the node having children (when the value is a Map).
From that point, I can of course write myself the simple recursion over the children, but I would like this part to be handled automatically (in an elegant way) through the use of a facility from the library (annotation, configuration, etc.), and not have to write the recursion by myself.
Note: we assume there is not name clashing in the different level keys.
There is no annotation-based mechanism to do this that I know of, since #JsonUnwrapped which might be applicable is only usable with POJOs, not Maps.
I guess you could file an enhancement request to ask #JsonUnwrapped to be extended to also handle Map case, although it seems only appicable for serialization (not sure how one could deserialize back).