spring MVC use #JsonView on spring-data Page - spring-mvc

I'm using Spring-MVC, Spring-data-jpa, jackson on a Jhipster project.
I managed to use the #JsonView annotation on an object and it works well when the method in the rest controller return a type ResponseEntity<List<MyObject>> but I can't make it work when the method return type is ResponseEntity<Page<MyObject>>.
I've tried to set MapperFeature.DEFAULT_VIEW_INCLUSION to true (which default is false). When I do it, all attributes are serialized. But filtering through #JsonView does not work anymore.
I can't modify the Page object because it's a Spring-data object.
I'm looking for a way to tell jackson to include all attributes of the Page object.
Here is my code:
My entity:
#Entity
#Table(name = "T_REGION")
public class Region implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "code", nullable = false)
private Integer code;
#Column(name = "name", length = 60, nullable = false)
#JsonView(View.Summary.class)
private String name;
// Getters and setters
}
My rest controller:
#RestController
#RequestMapping("/api")
public class RegionResource {
#RequestMapping(value = "/regionsearch1",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#JsonView(View.Summary.class)
public ResponseEntity<Page<Region>> findAll1(
#RequestParam(value = "page" , required = false) Integer offset,
#RequestParam(value = "per_page", required = false) Integer limit,
Sort sort)
throws URISyntaxException {
Pageable pageRequest = PaginationUtil.generatePageRequest(offset, limit, sort);
Page<Region> page = regionRepository.findAll(pageRequest);
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/regionsearch1", pageRequest);
return new ResponseEntity<>(page, headers, HttpStatus.OK);
}
#RequestMapping(value = "/regionsearch2",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#JsonView(View.Summary.class)
public ResponseEntity<List<Region>> findAll2(
#RequestParam(value = "page" , required = false) Integer offset,
#RequestParam(value = "per_page", required = false) Integer limit,
Sort sort)
throws URISyntaxException {
Pageable pageRequest = PaginationUtil.generatePageRequest(offset, limit, sort);
Page<Region> page = regionRepository.findAll(pageRequest);
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/regionsearch2", pageRequest);
return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK);
}
}
findAll1 returns:
[
{
"name": "Ile-de-France"
},
{
"name": "Champagne-Ardenne"
},
....
]
findAll2 returns:
{}
The object Page has no #JsonView on its attributes therefore no attributes are serialized.
I can't find a way to tell Jackson to include all Page attributes even when #JsonView is used.
Any ideas ?

Another way to return all page elements is to create your own implementation for the Page interface (including the JsonView you want):
JsonPage
public class JsonPage<T> extends org.springframework.data.domain.PageImpl<T> {
public JsonPage(final List<T> content, final Pageable pageable, final long total) {
super(content, pageable, total);
}
public JsonPage(final List<T> content) {
super(content);
}
public JsonPage(final Page<T> page, final Pageable pageable) {
super(page.getContent(), pageable, page.getTotalElements());
}
#JsonView(JsonViews.UiView.class)
public int getTotalPages() {
return super.getTotalPages();
}
#JsonView(JsonViews.UiView.class)
public long getTotalElements() {
return super.getTotalElements();
}
#JsonView(JsonViews.UiView.class)
public boolean hasNext() {
return super.hasNext();
}
#JsonView(JsonViews.UiView.class)
public boolean isLast() {
return super.isLast();
}
#JsonView(JsonViews.UiView.class)
public boolean hasContent() {
return super.hasContent();
}
#JsonView(JsonViews.UiView.class)
public List<T> getContent() {
return super.getContent();
}
}
Next return this class to the controller layer:
Service
#Override
public Page<User> findAll(final int page) {
PageRequest pr = new PageRequest(page, pageSize, new Sort(Sort.Direction.DESC, "dateCreated"));
return new JsonPage<User>(userRepository.findAll(pr), pr);
}
Controller
#JsonView(JsonViews.UiView.class)
#RequestMapping(method = RequestMethod.GET, value = "{page}")
public Page<User> getUsers(#PathVariable final int page) {
return userService.findAll(page);
}

I have done like this , it's working well
package com.natixis.spring.ws.configuration;
import java.io.IOException;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.Page;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
#Configuration
public class JacksonAdapter extends WebMvcConfigurerAdapter {
#Bean
public Jackson2ObjectMapperBuilder jacksonBuilder() {
return new Jackson2ObjectMapperBuilder()
.failOnUnknownProperties(false)
.serializationInclusion(Include.NON_EMPTY)
.serializerByType(Page.class, new JsonPageSerializer());
}
public class JsonPageSerializer extends JsonSerializer<Page<?>>{
#Override
public void serialize(Page<?> page, JsonGenerator jsonGen, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
ObjectMapper om = new ObjectMapper()
.disable(MapperFeature.DEFAULT_VIEW_INCLUSION)
.setSerializationInclusion(Include.NON_EMPTY);
jsonGen.writeStartObject();
jsonGen.writeFieldName("size");
jsonGen.writeNumber(page.getSize());
jsonGen.writeFieldName("number");
jsonGen.writeNumber(page.getNumber());
jsonGen.writeFieldName("totalElements");
jsonGen.writeNumber(page.getTotalElements());
jsonGen.writeFieldName("last");
jsonGen.writeBoolean(page.isLast());
jsonGen.writeFieldName("totalPages");
jsonGen.writeNumber(page.getTotalPages());
jsonGen.writeObjectField("sort", page.getSort());
jsonGen.writeFieldName("first");
jsonGen.writeBoolean(page.isFirst());
jsonGen.writeFieldName("numberOfElements");
jsonGen.writeNumber(page.getNumberOfElements());
jsonGen.writeFieldName("content");
jsonGen.writeRawValue(om.writerWithView(serializerProvider.getActiveView())
.writeValueAsString(page.getContent()));
jsonGen.writeEndObject();
}
}
}
Regards,
Régis LIMARE

I know this is an old question, but you can use something like this for a Page of objects
#Configuration
public class JacksonAdapter implements WebMvcConfigurer {
#Bean
public Jackson2ObjectMapperBuilder jacksonBuilder() {
return new Jackson2ObjectMapperBuilder().failOnUnknownProperties(false).serializerByType(Page.class,
new JsonPageSerializer());
}
public class JsonPageSerializer extends JsonSerializer<Page> {
#Override
public void serialize(Page page, JsonGenerator jsonGen, SerializerProvider serializerProvider)
throws IOException {
ObjectMapper om = new ObjectMapper().disable(MapperFeature.DEFAULT_VIEW_INCLUSION);
jsonGen.writeStartObject();
jsonGen.writeFieldName("size");
jsonGen.writeNumber(page.getSize());
jsonGen.writeFieldName("number");
jsonGen.writeNumber(page.getNumber());
jsonGen.writeFieldName("totalElements");
jsonGen.writeNumber(page.getTotalElements());
jsonGen.writeFieldName("last");
jsonGen.writeBoolean(page.isLast());
jsonGen.writeFieldName("totalPages");
jsonGen.writeNumber(page.getTotalPages());
jsonGen.writeObjectField("sort", page.getSort());
jsonGen.writeFieldName("first");
jsonGen.writeBoolean(page.isFirst());
jsonGen.writeFieldName("numberOfElements");
jsonGen.writeNumber(page.getNumberOfElements());
jsonGen.writeFieldName("content");
jsonGen.writeRawValue(
om.writerWithView(serializerProvider.getActiveView()).writeValueAsString(page.getContent()));
jsonGen.writeEndObject();
}
}
}

I've encountered the same problem and I solved it by setting MapperFeature.DEFAULT_VIEW_INCLUSION to true, but you should annotate all fields in classes where you want to apply your view with JsonView or JsonIgnore annotation so they wouldn't be included by default in json.

Related

Spring controller inheritance and linked requestbody deserealization problems

My objectmapper not working when I use spring controller & class for requestbody inheritation .
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type", visible = true)
#JsonSubTypes({
#JsonSubTypes.Type(value = RecipeVersion.class, name = "recipe"),
#JsonSubTypes.Type(value = DietVersion.class, name = "diet"),
})
public interface DocumentVersion {
Info getInfo();
void setInfo(Info info);
}
and also
#Data
public class DietVersion implements DocumentVersion {
private LocalizedText warnings;
private List<DietDay> days = new LinkedList<>();
private Info info = new Info();
private String getType() {
return "diet";
}
}
Ok. I have BaseController for diets and recipes
abstract public class BaseController<T extends Document<V>, V extends DocumentVersion> {
abstract protected BaseService<T, V> getService();
#PostMapping("/{docId}/version/last")
#ResponseStatus(HttpStatus.NO_CONTENT)
public void saveVersion(#PathVariable("docId") String docId, #RequestBody V version, Authentication authentication) {
getService().replaceLastVersion(docId, version, authentication);
}
}
and some realizations. example for diet
#Controller
#RequestMapping("/diet")
public class DietController extends BaseController<Diet, DietVersion> {
private final DietService dietService;
#Autowired
public DietController(DietService dietService) {
this.dietService = dietService;
}
#Override
protected DietService getService() {
return dietService;
}
#Override
public void saveVersion(String docId, DietVersion version, Authentication authentication) {
super.saveVersion(docId, version, authentication);
}
}
But when I send json with info, days, type ('diet') to '/diet/1/version/last' then I see in debug mode that my DietVersion pure clear and has no any data. Why ?
How to change settings for objectmapper ?
what if you provide all this in your DietController class.
public void saveVersion(#PathVariable("docId") String docId, #RequestBody V version, Authentication authentication){

Post entity returning null for testresttemplate

I am doing my integration test using test rest template but for postForEntity method I am getting null as response body, but I saw examples which were working fine on that but I am unable to resolve my issue,
PFB my test case
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ActiveProfiles("test")
public class PersonControllerTests {
#Autowired
private TestRestTemplate restTemplate;
#MockBean
private PersonRepository mockRepository;
#Before
public void init() {
List<Person> list = new ArrayList<>();
Person p1 = new Person("dumm1", "lastName1",22);
Person p2 = new Person("dumm2", "lastName2",32);
p1.setId(1l);
list.add(p2);
list.add(p1);
when(mockRepository.findAll()).thenReturn(list);
when(mockRepository.findById(1l)).thenReturn(Optional.of( p1 ));
}
#Test
public void createPerson() throws Exception {
Person p = new Person("dummy1", "dumm2", 11);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Person> httpEntity = new HttpEntity<Person>(p,headers);
ResponseEntity<String> response = restTemplate
.withBasicAuth("user", "password")
.postForEntity("/persons/create", httpEntity, String.class);
assertEquals(HttpStatus.OK, response.getStatusCode());
//assertEquals(MediaType.APPLICATION_JSON, response.getHeaders().getContentType());
assertEquals(11, response.getBody());
}
PFB my original code:
#RestController
#RequestMapping("/persons")
public class PersonController {
#Autowired
PersonRepository personRepository;
#GetMapping("/all")
public Iterable<Person> getAllUser() {
return personRepository.findAll();
}
#PostMapping(value ="/create",consumes = MediaType.APPLICATION_JSON_VALUE)
public Person createUser( #RequestBody Person person) {
return personRepository.save(person);
}
}
I believe I am doing some silly mistake but unable to understand
You are using #MockBean for your PersonRepository. Without any stubbing setup, Mockito returns null for reference types.
That's why return personRepository.save(person); returns null during your test.
You can stub your mock to simulate its behavior:
// import static org.mockito.ArgumentMatchers.any;
when(personRepository.save(ArgumentMatchers.any(Person.class))).thenAnswer(invocation -> {
Person person = invocation.getArgument(0);
// I assume you have an id field
person.setId(42L);
return person;
});

How to preserve HttpServletRequest autowiring when testing with SpringMVC Mockito and PowerMockito

Hi I'm trying to write some tests with Mockito and PowerMockito (I need to mock private methods) for a rest service written with SpringMVC and I'm facing the following issue
This is the semplified version of the controller
#Controller
#RequestMapping(value = "/test")
public class SimpleController {
#Autowired
private HttpServletRequest httpRequest;
#RequestMapping(value = "/simpleservice", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
#ResponseBody
public SimpleServiceResponse simpleService(#RequestBody SimpleServiceRequest simpleServiceRequest, HttpServletRequest httpServletRequest) {
SimpleServiceResponse simpleServiceResponse=new SimpleServiceResponse(simpleServiceRequest.getValue());
httpRequest.getHeader("Header");
return simpleServiceResponse;
}
}
and this is the correspoding test class
#WebAppConfiguration
#RunWith(PowerMockRunner.class)
#PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:WebApplicationContext.xml","classpath:SimpleApplicationContext.xml"})
#PrepareForTest(WebController.class)
#TestExecutionListeners({DependencyInjectionTestExecutionListener.class})
public class TestSimpleControllerMockito {
private Logger logger = LoggerFactory.getLogger(TestSimpleControllerMockito.class.getName());
private ObjectMapper objectMapper= new ObjectMapper();
#InjectMocks
private SimpleController controller;
#Test
public void testSimpleService() throws Exception {
MockitoAnnotations.initMocks(this);
SimpleService mockedSimple = mock(SimpleService.class);
when(mockedSimple.doSimpleService(any(SimpleServiceRequest.class))).thenReturn(new SimpleServiceResponse("MockMock"));
SimpleController mockedController=PowerMockito.spy(controller);
SimpleServiceRequest simpleServiceRequest= new SimpleServiceRequest("ciao");
String requestAsStr=objectMapper.writeValueAsString(simpleServiceRequest);
MockMvc mMockMvc=MockMvcBuilders.standaloneSetup(mockedController).build();
MvcResult result= mMockMvc.perform(post("/test/simpleservice").content(requestAsStr).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andReturn();
String content = result.getResponse().getContentAsString();
SimpleServiceResponse simpleServiceResponse=objectMapper.readValue(content,SimpleServiceResponse.class);
Assert.assertEquals("MockMockMockedSessionManager",simpleServiceResponse.getValue());
}
}
When running the test case I got a NullPointerEception on httpRequest.getHeader("Header");
My guess is that using the #InjectMocks annotation and then using
SimpleController mockedController=PowerMockito.spy(controller);
is the cause of the NullPointerException. I don't know how to preserve the #Autowire annotation processing on the controller Object. I already found a workaround, but it requires to write some redundant code.
Is there a way to make the #autowired annotation work?
Thanks a lot.
P.S.
the SimpleServiceXXX classes are like this one:
public class SimpleServiceResponse {
private String value;
public SimpleServiceResponse() {
}
public SimpleServiceResponse(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}

Error While Fetching Data from Corda Custom Tables

How to fetch data from corda Custom tables?
my sample code is as follows :-
Api layer -- getIous() method
{
Field attributeValue=IOUSchemaV1.PersistentIOU.class.getDeclaredField("value");
CriteriaExpression currencyIndex = Builder.equal(attributeValue, "12");
QueryCriteria.VaultCustomQueryCriteria criteria = new
QueryCriteria.VaultCustomQueryCriteria(currencyIndex);
vaultStates = services.vaultQueryByCriteria(criteria,IOUState.class);
}
In ExamplePlugin I added below code for schema registration
public class ExamplePlugin extends CordaPluginRegistry implements
WebServerPluginRegistry
{
#NotNull
#Override
public Set<MappedSchema> getRequiredSchemas()
{
Set<MappedSchema> requiredSchemas = new HashSet<>();
requiredSchemas.add(new IOUSchemaV1());
return requiredSchemas;
}
}
My Schema classes are ---
public final class IOUSchema {
}
#CordaSerializable
public class IOUSchemaV1 extends MappedSchema
{
public IOUSchemaV1() {
super(IOUSchema.class, 1, ImmutableList.of(PersistentIOU.class));
}
#Entity
#Table(name = "iou_states")
public static class PersistentIOU extends PersistentState {
#Column(name = "sender_name") private final String senderName;
#Column(name = "recipient_name") private final String recipientName;
#Column(name = "value") private final int value;
public PersistentIOU(String senderName, String recipientName, int value) {
this.senderName = senderName;
this.recipientName = recipientName;
this.value = value;
}
public String getSenderName() {
return senderName;
}
public String getRecipientName() {
return recipientName;
}
public int getValue() {
return value;
}
}
}
my state has :-
public class IOUState implements LinearState, QueryableState
{
--- some code goes here and below methods as well.---
#Override
public PersistentState generateMappedObject(MappedSchema schema) {
if (schema instanceof IOUSchemaV1) {
return new IOUSchemaV1.PersistentIOU(
this.sender.getName().toString(),
this.recipient.getName().toString(),
this.iou.getValue());
} else {
throw new IllegalArgumentException("Unrecognised schema $schema");
}
}
#Override
public Iterable<MappedSchema> supportedSchemas() {
return ImmutableList.of(new IOUSchemaV1());
}
}
But all the time i am getting below exception.
Caused by: net.corda.core.node.services.VaultQueryException:
Please register the entity 'com.example.schema.IOUSchemaV1' class in your CorDapp's CordaPluginRegistry configuration (requiredSchemas attribute)
and ensure you have declared (in supportedSchemas()) and mapped (in generateMappedObject())
the schema in the associated contract state's QueryableState interface implementation.
Can anyone please help to resolve this.
Try deleting implements WebServerPluginRegistry from your plugin declaration.

java.lang.NoSuchMethodError on service call

I have below codes as shown below
#Repository
public interface SaleRepository extends GraphRepository<Sale> {
#Query("MATCH (n:Sale) WHERE n.userId = {userId} RETURN n")
Iterable<Sale> getSaleByUserId(#Param("userId") String userId);
}
Sale.JAva
#NodeEntity
public class Sale extends Entity implements Serializable{
private Date createdDate;
public Date getCreatedDate() {
return createdDate;
}
public void setCreatedDate(Date createdDate) {
this.createdDate = createdDate;
}
}
SaleData.java
#RequestMapping(path = "/connect/{userId}", method = RequestMethod.POST, consumes = "application/json", produces = "application/json")
public String SaleData(#PathVariable final String userId)
{
Sale saleObject = null;
Iterable<Sale> saleData = SaleRepository.getSaleByUserId(String.valueOf(userPairInfo.getUserId()));
if(saleData .iterator().hasNext())
{
saleObject= saleData .iterator().next();
}
return "{\"userId\":\""+saleObject.getCreatedDate()+"\"}"; // on service call this gives me above error
}
if i call the service i get error,
java.lang.NoSuchMethodError: com.project.graphdb.neoj.domain.Sale.getCreatedDate()Ljava/util/Date
Its mainly tell because of sale.java file what else i am missing here?
Please help on this i am new to java and Neo4j
Ï think you are using java.util.Date and not java.sql.Date or you might need to recompile your source code.

Resources