Post JSON to Rest Spring 4 service - json

I am trying to post a complex/nested json to Rest Spring4 using PostMan rest client but objectMapper.readValue returns null.
#RequestMapping(value = "/loginuser", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody
Status Login(#RequestBody String userdata) {
try {
ObjectMapper objectMapper = new ObjectMapper();
LoginData theUser = objectMapper.readValue(userdata, LoginData.class);
String userdata contains Json string but objectMapper.readValue returns null.
JSON {"LoginData":{"id":"1", "username":"kashmir1","password":"kashmir2"}}
POJO:
public class LoginData implements Serializable{
#Id
#GeneratedValue
#Column(name = "id")
#JsonProperty("id")
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
and soon for username and password
Please provide inputs

I got answer :
follow below lines instead of directly accessing required node
JsonNode rootNode = objectMapper.readValue(jsonString, JsonNode.class) ;
JsonNode userNode=rootNode.path("LoginData");
LoginData theUser = objectMapper.treeToValue(userNode, LoginData.class);
System.out.println("In Login"+theUser.getUsername());

Related

Spring-Boot Post-Mapping with MultipartFile and Json Data

I want to create a REST API to place Files using Spring-Boot. Together with the file I need to get some Metadata which I would like to get as JSON class. At fist I created a class for the metadata:
#Getter
#Setter
#ToString
#RequiredArgsConstructor
#Entity
#Table(name = FileData.TABLE_NAME)
public class FileData {
public static final String TABLE_NAME = "file_data";
private static final long serialVersionUID = 1342709296259278462L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#GenericGenerator(name = "native", strategy = "native")
#Column(updatable = false)
private Long id;
#Column(updatable = false)
private String userId;
#Column(updatable = false, nullable = false)
private String filename;
private String description;
}
Then I created an API:
#RequestMapping("/files")
public interface FileApi {
#Operation(summary = "Add a new File and return its id.", tags = {"File Upload"})
#PostMapping(path = "/upload",
consumes = {MediaType.MULTIPART_FORM_DATA})
ResponseEntity<String> addFile(#RequestPart(value = "file") final MultipartFile file, #RequestBody FileData fileData, #Context final HttpServletRequest request);
}
I also implemented the method in my Controller. Now I am failing to implement a test for this method. I did following:
#Test
public void uploadFile_success() throws Exception {
final MockMultipartFile file = new MockMultipartFile("file", "test.zip", "text/plain", "test".getBytes());
FileData fileData = new FileData();
fileData.setFilename("bla.zip");
fileData.setDescription("A very nice File");
String address = "/files/upload";
mockMvc.perform(MockMvcRequestBuilders.multipart(address)
.file(file)
.content(MAPPER.writeValueAsString(fileData))
.contentType(MediaType.MULTIPART_FORM_DATA))
.andExpect(status().isOK())
.andExpect(content().string("OK"))
.andReturn();
}
When I run the test, I get HTTP error code 415 and the following error message:
Content type 'multipart/form-data' not supported
Regarding Swagger UI the Request Body shall have multipart/fomr-data. I also removed the contentType call (with same result) and changed it to "application/json" (same result, but with this content type).
Can andybody tell me what content type I have to set?
I could run the test with contentType "application/json" by removing the consumes entry in PostMapping, but in Swagger UI I have then to place the data of the file in a string field in JSON data and cannot transfer a binary file. I think this is not the right way to do it...

In #SpringBootTest, how do I get a fasterxml objectMapper to include a field from my model?

I'm using Spring Boot 2.1 with Java 11. I have annotated my User model with fasterxml annotations so that my password can be accepted for POST requests, but not returned for other REST requests ...
#Data
#Entity
#Table(name = "Users")
public class User implements UserDetails {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
private String firstName;
private String lastName;
#NotBlank(message = "Email is mandatory")
#Column(unique=true)
private String email;
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private String password;
private boolean enabled;
private boolean tokenExpired;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(
name = "users_roles",
joinColumns = #JoinColumn(
name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(
name = "role_id", referencedColumnName = "id"))
private Collection<Role> roles;
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
// TODO Auto-generated method stub
return null;
}
#Override
public String getUsername() {
return email;
}
#Override
public boolean isAccountNonExpired() {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean isAccountNonLocked() {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean isCredentialsNonExpired() {
// TODO Auto-generated method stub
return false;
}
#PrePersist #PreUpdate
private void prepare(){
this.email = this.email.toLowerCase();
}
}
However, when trying to run an integration test, the password is not getting translated by "objectMapper.writeValueAsString". Here is my test ...
#SpringBootTest(classes = CardmaniaApplication.class,
webEnvironment = WebEnvironment.RANDOM_PORT)
public class UserControllerIntegrationTest {
#Autowired
private TestRestTemplate restTemplate;
#Autowired
private ObjectMapper objectMapper;
#Autowired
private IUserRepository userRepository;
#Test
#WithUserDetails("me#example.com")
void registrationWorksThroughAllLayers() throws Exception {
final String email = "newuser#test.com";
final String firstName = "first";
final String lastName = "last";
final String password = "password";
User user = getTestUser(email, password, firstName, lastName, Name.USER);
ResponseEntity<String> responseEntity = this.restTemplate
.postForEntity("http://localhost:" + port + "/api/users", user, String.class);
assertEquals(201, responseEntity.getStatusCodeValue());
final User createdUser = userRepository.findByEmail(email);
assertNotNull(createdUser);
assertNotNull(createdUser.getPassword());
}
#Test
#WithUserDetails("me#example.com")
void getDetailsAboutMyself() throws JsonProcessingException, JSONException {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
UserDetails user = (UserDetails) authentication.getPrincipal();
final User foundUser = userRepository.findByEmail(user.getUsername());
ResponseEntity<String> responseEntity = this.restTemplate
.getForEntity("http://localhost:" + port + "/api/users/" + foundUser.getId(), String.class);
assertEquals(200, responseEntity.getStatusCodeValue());
// assert proper response
final String userAsJson = objectMapper.writeValueAsString(user);
assertEquals(userAsJson, responseEntity.getBody());
JSONObject object = (JSONObject) new JSONTokener(userAsJson).nextValue();
// Verify no password is returned.
assertNull(object.getString("password"));
}
...
}
The JSON from the objectMapper.writeValueAsString call is
{"id":null,"firstName":"first","lastName":"last","email":"newuser#test.com","enabled":true,"tokenExpired":false,"roles":null,"username":"newuser#test.com","authorities":null,"accountNonExpired":false,"accountNonLocked":false,"credentialsNonExpired":false}
What's the proper way to get my password included as part of the mapping as well as suppressing the password when requesting my entity from read endpoints?
This is a common misunderstanding, there was even a bug report for it and they clarified the documentation.
"READ" and "WRITE" are to be understood from the perspective of the Java Property, i.e., when you serialize an Object, you have to read the property and when you deserialize it, you have to write it.
In your case, you want #JsonProperty(access = JsonProperty.Access.READ_ONLY)
Using WRITE_ONLY or READ_ONLY will not work in your case. The reason is one call over http needs both. Lets take this.restTemplate.postForEntity as an example. In the sending side, your User java object need to serialised to json so it needs the READ and when the rest endpoint receives the json, it need to deserialise the json into User java object so needs the WRITE. It will be the same scenario for this.restTemplate.getForEntity too
One solution is to set the password field of User on the GET endpoint to null before returning
Another solution is create a separate UserDto without password field and return it from GET endpoint
Another solution is to create two JsonViews where one is with password and other one is without password. Then annotate your endpoints with correct #JsonView

How to GET data in the JSON format form the DB Repository

I have this JPA Class, where I have 3 columns id, name and date. The Database is already filled with data, where each entry has an id.
#Data
#Entity
#Table(name = "TEST", schema = "TESTSCHEMA")
public class TestDataJpaRecord implements Serializable {
private static final long serialVersionUID = 1L;
TestDataJpaRecord(){
// default constructor
}
public TestDataJpaRecord(
String name,
Date date,
){
this.name = name;
this.date = date;
}
#Id
#Column(name = "ID", nullable = false)
#GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "TEST_SEQUENCE")
#SequenceGenerator(
sequenceName = "TEST_SEQUENCE", allocationSize = 1,
name = "TEST_SEQUENCEx")
private Long id;
#Column(name = "NAME")
private String name;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "DATE")
private Date date;
}
I created a JPA repository for all the data.
public interface TestDataJpaRecordRepository extends JpaRepository<TestDataJpaRecord, Long> {
}
I want to get the data from the DB in a JSON format.
Here is my Rest GET Api. Here I return the data as a string just, but I want to return them as JSON.
#GetMapping(value = "data/{id}")
private ResponseEntity<?> getDataFromTheDB(#PathVariable("id") Long id) {
// get one entry form the DB
TestDataJpaRecord testDataJpaRecord =testDataJpaRecordRepository.findOne(id);
// Here I want to return a JSON instead of a String
return new ResponseEntity<>(testDataJpaRecord.toString(), HttpStatus.OK);
}
Any idea on how I could return the data as JSON and not as a string from the DB?
I would very very much appreciate any suggestion.
If you have Jackson on the classpath which you should if you have used the spring-boot-starter-web then simply:
#GetMapping(value = "data/{id}")
private ResponseEntity<TestDataJpaRecord> getDataFromTheDB(#PathVariable("id") Long id) {
TestDataJpaRecord testDataJpaRecord =testDataJpaRecordRepository.findOne(id);
return new ResponseEntity.ok(testDataJpaRecord);
}
This assumes you have annoted your controller with #RestController rather than #Controller. If not then you can either do that or, annotate your controller method with #ResponseBody.
With Spring Data's web support enabled (which it should be by default with Spring Boot) then you can also simplify as below:
#GetMapping(value = "data/{id}")
private ResponseEntity<TestDataJpaRecord>
getDataFromTheDB(#PathVariable("id") TestDataJpaRecord record) {
return new ResponseEntity.ok(record);
}
See:
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#core.web.basic.domain-class-converter

SpringBoot Rest response not deserialiazed with jackson

I am a running a project with SpringBoot. In this project I am calling an external Rest Service. I have modeled the response items into bean.
But when I get the response back the data are not serialised into the beans.
I guess there must be some configuration missing but I cannot find what.
I have added onfiguration spring-boot-starter-test to the configuration of Maven:
The rest client:
#RunWith(SpringRunner.class)
#SpringBootTest
public class RestClientTest {
#Autowired
private RestTemplateBuilder restTemplate;
#Test
public void sayHello() {
System.out.println("Hello");
assert(true);
}
#Test
public void testGetEmployee() {
RestTemplate template = restTemplate.build();;
HttpHeaders headers = new HttpHeaders();
List<MediaType> types = new ArrayList<MediaType>();
types.add(MediaType.APPLICATION_JSON);
types.add(MediaType.APPLICATION_XML);
headers.setAccept(types);
headers.set("Authorization", "Bearer gWRdGO7sUhAXHXBnjlBCtTP");
HttpEntity<Items> entity = new HttpEntity<Items>(headers);
String uri = "https://mytest.com/employees";
//ResponseEntity<String> rec = template.exchange(uri, HttpMethod.GET, entity, String.class);
//System.out.println("Received: " + rec);
ResponseEntity<Items> rec = template.exchange(uri, HttpMethod.GET, entity, Items.class);
System.out.println("Received: " + rec);
}
}
When I inspect the elements of the response it, I get a list, all the items are with null values
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public class Item implements Serializable {
#JsonProperty
private String id;
#JsonProperty
private String name;
#JsonProperty
private String email;
#JsonProperty
private String phone;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public class Items implements Serializable {
#JsonProperty
private List<Item> items = new ArrayList<Item>();
public List<Item> getItems() {
return items;
}
}
Do you see what I am missing here?
The response is like this:
{
"items": [
{
"item": {
"id": 0,
"name": "string",
"email": "string",
"phone": "string",
Do you see what I am missing here?
Thanks
Gilles
The way you have implemented will try to deserialize data into Items class. But it doesn't have the required properties to deserialize. When you need to get a list of data through rest template exchange, you can get them as follows.
Get data as an array and convert it into arrayList.
Item[] itemArray = template.exchange(uri, HttpMethod.GET, entity, Item[].class).getBody();
List<Item> itemList = Arrays,asList(itemArray);
or
Use ParameterizedTypeReference to get data as a list
ResponseEntity<List<Item>> itemList = template.exchange(uri, HttpMethod.GET, entity, new ParameterizedTypeReference<List<Item>>() {});
List<Item> itemList = template.exchange(uri, HttpMethod.GET, entity, new ParameterizedTypeReference<List<Item>>() {}).getBody(); // access list directly
You might need to add this to your ObjectMapper:
mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
And on your entity add #JsonRootName("item")

Json data is not wrapped from RESTEasy services

I implements test RESTEasy services. These are simple codes.
Member.java
#XmlAccessorType(XmlAccessType.NONE)
#XmlRootElement(name = "member")
public class Member implements Serializable{
#XmlElement(name = "id")
private String id;
#XmlElement(name = "passwd")
private String passwd;
#XmlElement(name = "age")
private int age;
#XmlElement(name = "name")
private String name;
public String getId() {
return id;
}....
REST Service java
#Path("/services")
public class RESTEasyServices {
#GET
#Path("getJson/{id}")
#Produces(MediaType.APPLICATION_JSON) // This config can't be understood
public Response sayJsonHello(#PathParam("id") String id){
Member m = new Member();
m.setId(id);
m.setPasswd("aaa");
m.setAge(45);
m.setName("joseph");
//return m;
return Response.status(200).entity(m).build();
}
}
Invocation URI is successful. But Json data is not wrapped:
{"id":"aupres","passwd":"aaa","age":45,"name":"joseph"}
However when I set the attribute of #Produce to MediaType.APPLICATION_XML like below,
#Produces(MediaType.APPLICATION_XML)
response of XML data is wrapped.
<member>
<id>aupres</id>
<passwd>aaa</passwd>
<age>45</age>
<name>joseph</name>
</member>
I have no idea how to wrap the json data.
This is solved. These are my codes
#GET
#Path("getJson/{id}")
#Produces(MediaType.APPLICATION_JSON)
public Map<String, List<Member>> sayJsonHello(#PathParam("id") String id){
Member m1 = new Member();
m1.setId(id);
m1.setPasswd("aaa");
m1.setAge(45);
m1.setName("joseph");
Member m2 = new Member();
m2.setId("hwa5383");
m2.setPasswd("bbb");
m2.setAge(40);
m2.setName("jina");
List<Member> list = new ArrayList();
list.add(m1);
list.add(m2);
Map<String, List<Member>> mems = new HashMap();
mems.put("member", list);
return mems;
}
Then the output is Json type
{"member":[{"id":"aupres","passwd":"aaa","age":45,"name":"joseph"},{"id":"hwa5383","passwd":"bbb","age":40,"name":"jina"}]}
Thanks any way.