Json data is not wrapped from RESTEasy services - json

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.

Related

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

When deserializing List of objects, JSON looks for a unexistent class [duplicate]

This question already has answers here:
Status 400 and Error deserializing List of Objects. No default constructor found
(2 answers)
Closed 4 years ago.
Using Spring I created a microservice that finds all messages in DB for a specific user.
Controller:
#RestController
public class Controller {
#Autowired
private MessageRepository daoMsg;
#RequestMapping(value = "/Mensajes", method = RequestMethod.GET, produces=MediaType.APPLICATION_JSON_VALUE)
public List<MessageObject> enviados (#RequestParam("mail") String mail) {
return daoMsg.findByEmisorOrDestinatario(mail, mail);
}
}
DAO:
public class MessageObject implements Serializable{
private static final long serialVersionUID = 1L;
#Id
private String id;
private String emisor;
private String destinatario;
private String mensaje;
private String tipo;
private LocalDate fecha;
private String id_housing;
public MessageObject() {
}
public MessageObject(String id, String emisor, String destinatario, String tipo, LocalDate fecha, String id_housing) {
this.id = id;
this.emisor = emisor;
this.destinatario = destinatario;
this.tipo = tipo;
this.fecha = fecha;
this.id_housing = id_housing;
}
When calling the microservice from my client app:
Client client = ClientBuilder.newClient();
WebTarget webResource =
client.target("http://localhost:8082").path("Mensajes").queryParam(mail);
Invocation.Builder invocationBuilder =
webResource.request(MediaType.APPLICATION_JSON);
Response respuesta = invocationBuilder.get();
int status = respuesta.getStatus();
System.out.println("el status es "+ status);
MessageObject[] listMessages =
respuesta.readEntity(MessageObject[].class);
stacktrace:
javax.ws.rs.ProcessingException: Error deserializing object from entity
stream.Caused by: javax.json.bind.JsonbException: Can't create instance of
a class: class [LMessages.MessageObject;, No default constructor
found.Caused by: java.lang.NoSuchMethodException:
[LMessages.MessageObject;.<init>()
My client has the same MessageObject DAO as the one in the microservice, in:
Question: why JSON is looking for a default constructor in LMessages.MessageObject if my MessageObject class is in package Messages (not LMessages)
Solved. Problem was i was using .queryparam(mail) without key - value structure, only key. Now using .queryparam ("mail", mail) works.

Jackson JSON with list attribute

I have the following 2 classes in my application:
public class GolfCourse {
private int id;
#JsonBackReference
private List<Hole> holes;
......
}
public class Hole{
private int id;
#JsonManagedReference
private GolfCourse course;
......
}
When I try to serialize a list of GolfCourse objects as JSON using Jackson:
List<GolfCourse> courses
......(populate course)
String outputJSON = new ObjectMapper().writeValueAsString(golfCourses);
I end up with a JSON array that only shows the id attribute for each of the golf courses, but it does not include the hole list:
[{"id":"9ed243ec-2e10-4628-ad06-68aee751c7ea","name":"valhalla"}]
I have verified that the golf courses all have holes added.
Any idea what the issue may be?
Thanks
I managed to get desired result by using #JsonIdentityInfo annotation:
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class GolfCourse
{
public int id;
public String name;
public List<Hole> holes;
}
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Hole
{
public int id;
public String name;
public GolfCourse course;
}
test method:
public static void main(String[] args) {
Hole h1 = new Hole();
Hole h2 = new Hole();
GolfCourse gc = new GolfCourse();
h1.id = 1;
h1.name = "hole1";
h1.course = gc;
h2.id = 2;
h2.name = "hole2";
h2.course = gc;
gc.id = 1;
gc.name = "course1";
gc.holes = new ArrayList<>();
gc.holes.add(h1);
gc.holes.add(h2);
ObjectMapper mapper = new ObjectMapper();
try {
mapper.writeValue(System.out, gc);
} catch (Exception e) {
e.printStackTrace();
}
}
output:
{"id":1,"name":"course1","holes":[{"id":1,"name":"hole1","course":1},{"id":2,"name":"hole2","course":1}]}

Post JSON to Rest Spring 4 service

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());