RestTemplate only get data I care about - spring

A RestTemplate request I am making gives a JSON request of a large data type. For example it responds with a channel object, a user object, and a preferences object.
I only need the information that relates to a channel object. Is there any way to have the responseType only deal with the channel object even though the response contains more than that?
I am only asking because I feel like it is pointless to create DTOs that contain fields that I don't care about and will never use.

You can create a pojo with channel object and mark pojo to ignore unknown properties ,
#JsonIgnoreProperties(ignoreUnknown = true)
public class Myclass {
Channel channel;
...
}
In rest template ,
restTemplate.exchange(url, HttpMethod.POST, entity, Myclass .class);

Related

Given an assignment to return specific data using Springboot reactive but the JSON is really complicated

I am new to Springboot reactive
I was asked to call the following endpoint and return todays weather data only:
https://api.weather.gov/gridpoints/MLB/33,70/forecast
I believe I need to use something like this...
WebClient.create().get()
.uri("https://api.weather.gov/gridpoints/MLB/33,70/forecast")
.retrieve()
.bodyToMono(WeatherClass.class)
.block();
Do I need to map out an entire java object to match the JSON at the endpoint? is there an easy way to perhaps just grab the a certain piece of the JSON?
How would I handle something like the #context annotation in the JSON.
The WebClient in spring boot automatically uses Jackson's ObjectMapper to unmarshall json to a java object when the content type of the response is application/json. So there is no need to pull in any additional libraries or have to write any specific unmarshalling code, unless you want to use an alternate json-to-java library.
When using Jackson, you don't need to map every field in the json to your java object. You can annotate your java class with #JsonIgnoreProperties to inform jackson to ignore any properties that may appear in the json but do not have a matching field in your java object.
An example WeatherClass in which you want only the #context and forecastGenerator unmarshalled would look something like this
#JsonIgnoreProperties
public class WeatherClass {
private final List<Object> context;
private final WeatherProperties weatherProperties;
public WeatherClass(#JsonProperty("#context") List<Object> context,
#JsonProperty("properties") WeatherProperties weatherProperties) {
this.context = context;
this.weatherProperties = weatherProperties;
}
private class WeatherProperties {
private final String forecastGenerator;
private WeatherProperties(#JsonProperty("forecastGenerator") String forecastGenerator) {
this.forecastGenerator = forecastGenerator;
}
}
}
Note
#context seems to be an array that can contain multiple types (both objects and strings in your example). I've used Object to work around this but obviously isn't the most graceful solution but should be adequate to demonstrate how Jackson works
Alternatively, you can unmarshall the response to a JsonNode, which you can then use to traverse the structure of the json without converting it to a java object. For example
String forecastGenerator = WebClient.create().get()
.uri("https://api.weather.gov/gridpoints/MLB/33,70/forecast")
.retrieve()
.bodyToMono(JsonNode.class)
.block().get("properties").get("forecastGenerator").toString()
There are many other annotations provided by Jackson that can used to define how the unmarshaller functions. Too many to cover here. See Jackson Deserialisation Annotations

Different class types as RequestBody depending on RequestParam provided in a Spring Boot Controller?

So, I have a controller which takes in a request parameter and a body. The request body can be of various class types depending on the type of parameter. Currently I am using JsonNode for the body which works fine. Looks like this :
#PostMapping() public ResponseEntity<Response> save(#RequestParam("request type") RequestProcess process, #Valid #RequestBody JsonNode requestJson) {
I want to know whether it's possible to provide the body with the class type depending on the param provided. If yes how do I do it?
If this is not possible in REST, is there a chance I might be able to do this using GraphQl. I don't know much about GraphQL still researching.
TIA
The nearer you can reach is by using generics
class Controller < T > {
#PostMapping("/save")
ResponseEntity < Response > save(#RequestBody T requestJson) {}
}

Access Post method json RequestBody parameters in webflux reacive programming

How to access RequestBody parameters which is of Mono type. Spring Boot Webflux reactive.
I would like to return a ResponseEntity and not a Mono.
#RequestMapping(value = "/poststudentdata", method = RequestMethod.POST, headers={"content-type=application/json"})
public ResponseEntity<String> poststudentData(#Valid #RequestBody Mono<Student> student, BindingResult bindingResult) {
// How can i access student.getName() etc.... RequestBodt parameters
// Not able to access as have declared Student as Mono.
}
Don't try to return a non-reactive type when your input is provided asynchronously via a reactive type (Mono), because it means you'll likely end up blocking the IO thread on which the request was processed, which assumes non-blocking behavior of Controllers. This brings up the risk of not only blocking the current request's processing, but processing of all other requests in the application.
So change the return type to Mono<ResponseEntity>, rename student to studentMono for clarity and process your student in a map (or possibly flatMap if you have asynchronous transformations to apply):
return studentMono.map(student -> ResponseEntity.ok(student.getName()));

How to prevent MappingJackson2XmlHttpMessageConverter from taking over serialization?

I'm using RestTemplate to interact with several REST services, some of them accept/return JSON and some XML. To that end, I've added Jackson's dataformat-xml module as a dependency (along with the JAXB annotations module). RestTemplate automatically includes MappingJackson2XmlHttpMessageConverter (done in the RestTemplate constructor).
This creates a situation where some objects that are used as the request parameter in calls to
RestTemplate.postForObject(String url, Object request, Class<T> responseType, Object... uriVariables)`
get serlialized as XML and the content-type of the request is set to application/xml. For example:
MyObject request = ...;
String url = ...;
MyResponseObject response = restTemplate.postForObject(url, request, MyResponseObject.class);
RestTemplate tries to serialize MyObject to XML and sets the request media type to application/xml.
Problem is, many of the services we call don't accept XML (they expect JSON). So now that I have MappingJackson2XmlHttpMessageConverter on the classpath, it's taking precedence over the JSON converter which makes the calls to JSON services fail.
I suppose I could change my calling code to pass an HttpEntity with the media type explicitly set to JSON instead of my simple data object, but that's kind of ugly (boilerplate) and would mean changing quite a few service calling code.
Is there a way to either
A) change the priority of the MessageConverters so that the standard Jackons (JSON) one takes priority over MappingJackson2XmlHttpMessageConverter
or
B) Prevent MappingJackson2XmlHttpMessageConverter from claiming that it can serialize the objects I don't want it to
?
I can see two options :
Create a RestTemplate with the HttpMessageConverter you want, in the order you want them to be used (check HttpEntityRequestCallback.doWithRequest they are used in the order they are in the list and the first matching converter will be used
As you suggested, using an HttpEntity and setting the Content-Type header to the mime type you want to get.
I think using a helper to create an HttpEntity with your object and the correct Content-Type header would be safer :
public class HttpEntityHelper {
public static <T> HttpEntity<T> jsonHttpEntity(T body) {
MultiValueMap<String, String> headers = new LinkedMultiValueMap();
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
return new HttpEntity(body, headers);
}
}

Resttemplate unable to parse array of type T

I'm facing a problem with Spring and restTemplate. I want to send an object (ListResponse) that contains a generic array. The defenition is as follow:
public class ListResponse<T> implements Serializable {
private long total;
private int page;
private int pageSize;
private T[] objects;
I send a request whith restTemplate.getForObject(). As a result I get an object of type ListResponse but the objects array contains an array of LinkedHashMaps instead of an array with objects of type T.
It seems like restTemplate can not convert the elements in the array to their correct type.
How can I make sure that I get an array of objects of type T back ?
I had this problem today and here is the solution that I came up with (actually, that one of my co-workers suggested). We use it with an interface that returns List<MyDto>.
When you call the RestTemplate, don't pass in the generic type.
Define: public class MyDtoListTemplate extends ListTemplate<MyDto>
Then, call
MyDtoListTemplate template = restTemplate.getForObject("url", MyDtoListTemplate .class, mapOfPathVariables);
It's a bummer that you have to define a concrete class that extends/implements the generic type, but then the generic information is available to the jackson deserializer.
I remember I was able to deserialize generic classes with Jackson 2. I had to add MappingJackson2HttpMessageConverter converter to RestTemplate before making any Http calls with it.
RestTemplate template = new RestTemplate();
template.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
Unfortunately there's no easy way of doing it that I know of. The problem is that the RestTemplate is told which object type to expect. As long as all the fields in this object has a corresponding element in the json/xml, everything works fine. In the case of generics, the serializer doesn't know which class to expect so it just turns the map it gets to a java Map.
You will have the same problem if you tried to getForObject for a generic return type.

Resources