Jax-RS API POST and GET methods (RESTful Services) - json

I have a project (homework) about JAX-RS. I'm working with NetBeans, Jersey and Tomcat. In the post method for example:
'{"user":{"username":"accavdar", "gender":"M", "birthDate":"06.11.1982"}}'
when such a request comes I have to parse this input and add new user to my system. The sample response must be:
{
"meta": {
"code": 200
},
"data": {
"message": "You successfully created a user."
}
}
Expectable error can be like that:
{
"meta": {
"code": 101,
"type": "FieldError",
"errors": [
{
"fieldName": "fullname",
"rejectedValue": null
}
]
}
}
Another problem is that: With using Get method the develepor can want to list all user in the system. The response must be like that :
{
"meta": {
"code": 200
},
"data": [
{
"id": 1,
"username": "sample",
"fullname": "sample",
"gender": "M",
"birthDate": "12.02.1990"
},
{
"id": 2,
"username": "sample",
"fullname": "sample",
"gender": "M",
"birthDate": "21.09.1940"
}
]
}
I want to keep users in a text file, there is no limitation about the way of keeping users.(You can keep in database or in memory) But I have no idea about how to handle request input and generate a response like that. I don't want you to do my homework but could anyone give tips about my problem, please?
NOTE: We will work only with JSON "Content-Type: application/json" "Accept: application/json"
EDİT: #Bogdan , Thank you very much for your answer. I searched the web sites you gave. I want to learn that how is that output produced?:
{
"meta": {
"code": 200
},
"data": {
"message": "You successfully created a user."
}
}
or
{
"meta": {
"code": 200
},
"data": {
"id": 1,
"username": "jack",
"fullname": "jack",
"gender": "M",
"birthDate": "12.12.2012"
}
}
I have "meta" and "user" classes.
#XmlRootElement(name="data")
public class User {
#XmlElement
public int id ;
#XmlElement
public String username;
#XmlElement
public String fullname;
#XmlElement
public String gender;
#XmlElement
public String birthDate;
public User(){
}
#XmlRootElement(name="meta")
public class Meta {
#XmlElement
int code=200;
public Meta(){
}
Also I have this jaxbcontextresolver class
#Provider
public class JAXBContextResolver implements ContextResolver<JAXBContext>{
private JAXBContext context;
private Class[] types = {User.class, Meta.class};
public JAXBContextResolver() throws Exception {
this.context =
new JSONJAXBContext( JSONConfiguration.mapped().nonStrings("id").nonStrings("code").build(), types);
}
#Override
public JAXBContext getContext(Class<?> objectType) {
for (Class type : types) {
if (type == objectType) {
return context;
}
}
return null;
}
}
but how to create this response constructs, could any help me?

Your application works with users. This is the resource that you application deals with and your client interacts with for creating, updating, deleting and getting (basically CRUD).
But a user is an abstract resource so your server and client interact with each other by using representations of this resource. The representations can be in JSON format (as in your example), XML etc. Your client specifies what type of representation it wants and the server specifies the representation type it returns by means of the Content-Type. The body of the request/response matches the content type.
This is a Java application so in your application code the users are represented as Java objects. You need to transform the request/response body into objects with getters and setters. You can use JAXB to do the XML transformation, and with a library like Jackson you can transform JSON. You can off course do it manually and parse strings into objects and output strings from objects but that would be more work.
A client submits JSON and after transformation you will have your Java objects that you can handle. You can keep them in memory inside an application scope map or write them inside a file or database and change their representation once again.
Your application will bind URLs to specific actions that transform the request representation into objects, perform operations on the objects then return them to be transformed again into the representation the client expects.
The above are just basic explanations. All your questions can be answered if you follow some JAX-RS tutorials (e.g. a quick search returns REST with Java (JAX-RS) using Jersey - Tutorial or REST: CRUD with JAX-RS (Jersey). I'm sure there are lots other). Your question is too open ended so just dig in and return with specific questions on stackoverflow when you hit road blocks.
EDIT : seems you are struggling with this a little so I'll add a basic service to get you started, let's say for the list of users.
You didn't mention so far nothing about your service class. That's the important one, something like:
package com.test;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
#Path("/api")
public class Test {
#GET
#Path("/users")
#Produces({ MediaType.APPLICATION_JSON })
public UsersResponseWrapper getUsers() {
List<User> users = new ArrayList<User>();
User u1 = new User();
u1.setId(1);
u1.setFullname("Joe Doe");
u1.setGender("M");
u1.setUsername("joe.doe");
u1.setBirthDate("1919-12-12");
User u2 = new User();
u2.setId(1);
u2.setFullname("John Smith");
u2.setGender("M");
u2.setUsername("john.smith");
u2.setBirthDate("1990-01-01");
users.add(u1);
users.add(u2);
UsersResponseWrapper resp = new UsersResponseWrapper();
resp.setMeta(new Meta(200));
resp.setData(users);
return resp;
}
}
Then your user and meta classes:
package com.test;
public class Meta {
private int code;
public Meta(int code) {
this.code = code;
}
public Meta() {
this.code = 200;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
}
package com.test;
public class User {
private int id;
private String username;
private String fullname;
private String gender;
private String birthDate;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getFullname() {
return fullname;
}
public void setFullname(String fullname) {
this.fullname = fullname;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getBirthDate() {
return birthDate;
}
public void setBirthDate(String birthDate) {
this.birthDate = birthDate;
}
}
A JAXB provider:
package com.test;
import java.util.ArrayList;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import javax.xml.bind.JAXBContext;
#Provider
public class JAXBContextResolver implements ContextResolver<JAXBContext> {
private JAXBContext context;
private static Class<?>[] types = {UsersResponseWrapper.class, User.class, Meta.class, ArrayList.class};
public JAXBContextResolver() throws Exception {
this.context = JAXBContext.newInstance(types);
}
#Override
public JAXBContext getContext(Class<?> objectType) {
for (Class<?> type : types) {
if (type == objectType) {
return context;
}
}
return null;
}
}
Something from web.xml:
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<servlet>
<servlet-name>RestService</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.test</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>RestService</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
And a wrapper for your result:
package com.test;
import java.util.List;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class UsersResponseWrapper {
private Meta meta;
private List<User> data;
public Meta getMeta() {
return meta;
}
public void setMeta(Meta meta) {
this.meta = meta;
}
public List<User> getData() {
return data;
}
public void setData(List<User> data) {
this.data = data;
}
}
I think this last class is what put you in difficulty since your result is formed of both meta content and data content. Remember you need to return objects (the default Jackson mapper from Jersey distribution will then take care of it) and it happens you have a complex one. The above should return this (formatting not included):
{
"data": [
{
"birthDate": "1919-12-12",
"fullname": "Joe Doe",
"gender": "M",
"id": "1",
"username": "joe.doe"
},
{
"birthDate": "1990-01-01",
"fullname": "John Smith",
"gender": "M",
"id": "1",
"username": "john.smith"
}
],
"meta": {
"code": "200"
}
}
That's about as much I can add to this as details. It's your homework after all :). You are doing fine, just keep going.

Related

spring boot json to object mapper with complicated json

I have this list.json that I need to read to mapper object,
{
"name":"first",
"identity":"gold",
"code":{
"csharp":{
"input":"sample of csharp code",
"value":[
{
"main":"true",
"power":"low"
},
{
"main":"false",
"power":"low"
}
],
"description":"description of csharp code",
"manager":"bill gates"
},
"java":{
"input":"sample of java",
"value":[
{
"main":"true",
"power":"low"
},
{
"main":"false",
"power":"high"
},
{
"main":"true",
"power":"low"
}
],
"description":"description of java",
"manager":"steve job"
}
}
},
{
"name":"second",
"identity":"diamond",
"code":{
"python":{
"input":"sample of python code",
"new":"make it more complicated with new parm not value", // do not forget this
"description":"description of python code",
"manager":"john doe"
},
"csharp":{
"input":"sample of csharp code",
"value":[
{
"main":"true",
"power":"low"
},
{
"main":"false",
"power":"low"
}
],
"description":"description of csharp code",
"manager":"bill gates"
},
}
I omit the long list, I only put two base or outter array, but basically its about 200 or more records.
The List.class,
#Data
#AllArgsConstructor
#Entity
public class List {
private String name;
private String identity;
#OneToOne(cascade = CascadeType.ALL)
private Code[] code;
public List() {}
}
Is the Code[] correct and also onetoone or onetomany?
The Code.class,
#Data
#AllArgsConstructor
#Entity
public class Code {
<<I have no idea what to put here>>
}
Do I need to put any string variable for csharp, java, pyhton? They key should be the same as the variable in the class? But how do you do that since it's not constant?
There's a dynamic 2-layer json here in baeldung but how do I do that in the 3-layer?
Here's I got, you have to use JsonNode for the rest of the layers.
I didn't use this annotation for now, don't want to struggle for now, just add getter/setter and constructor using fields, maybe something to do with java 8,
#Data
#AllArgsConstructor
#Entity
#OneToOne(cascade = CascadeType.ALL)
So I remove it. Also how I did it, you have to simulate one by one in the json, meaning I have to add name and identity since those two are similar, if it works, then I add the code as JsonNode.
public class List {
private String name;
private String identity;
JsonNode code;
public List() {}
// put getter/setter
// put constractors as fields
}
Then on your controller,
private String strJson = null;
#PostConstruct
private void loadData() {
ClassPathResource classPathResource = new ClassPathResource("json/list.json");
try {
byte[] binaryData = FileCopyUtils.copyToByteArray(classPathResource.getInputStream());
strJson = new String(binaryData, StandardCharsets.UTF_8);
} catch (IOException e) {
e.printStackTrace();
}
ObjectMapper objectMapper = new ObjectMapper();
DataModel datam = null;
try {
datam = objectMapper.readValue(strJson, List.class);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(datam.code()[0].get("csharp").get("value").get("main"); // output = "true"
Thanks to Baeldung for the idea.

How to specify the endoint of a REST API accepting a list of types using a supertype?

I intend to specify a REST endpoint in a Spring Boot application which accepts a list of objects (List<? extends SuperObject>). SuperObject is an abstract base class. The lists sent within the RequestBody of an HttpRequest keeps instances of SuperObject's sub-/childclasses (SubType1OfSuperObject and SubType2OfSuperObject).
Currently I tried this:
#PostMapping(path = "store", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.OK)
#ResponseBody
public String store(#RequestBody List<? extends SuperObject> oject, #RequestParam Technology technology) {
theService.saveObjectsByTechnology(objects, technology);
return "Perfect!";
}
I tried to send a list of subtype objects using Postman:
{
"subtype1OfSuperObjects": [
{
"prop1": 4,
"prop2": "foo",
"prop3": "xxx"
},
{
"prop1": 7,
"prop2": "Bar",
"prop3": "zzz"
}
]
}
The result is
{
"timestamp": "2021-08-18T12:24:55.797+00:00",
"status": 400,
"error": "Bad Request",
"path": "/admin/error/store"
}
The SuperObject class:
#Data
#SuperBuilder
#NoArgsConstructor
#Validated
public abstract class SuperObject {
private Integer prop1;
private String prop2;
}
The SubType1OfSuperObject class:
#Data
#SuperBuilder
#NoArgsConstructor
#Validated
public class SubType1OfSuperObject extends SuperObject {
private String prop3;
}
The SubType1OfSuperObject class:
#Data
#SuperBuilder
#NoArgsConstructor
#Validated
public class SubType2OfSuperObject extends SuperObject {
private String prop4;
}
How can I achieve this. The classes of the supertype and the subtypes are implemented, of course.
The REST API is accessible as I can invoke another (GET) endpoint (to list all subtypes, for instance).
If I understand it well you have a list of data to send? if that's the case you can have something like below:
In your controller
#PostMapping("/")
#ResponseBody
public String save(#RequestBody List<Data> data){
System.out.println(data);
return "good";
}
And you define your Data pojo like this
class Data{
private List<SubData> subtype1OfSuperObjects;
//getters, setters and toString
}
class SubData{
private int prop1;
private String prop2;
// getters, setters and toString
}
Here is the result of by sysout
[Data{subtype1OfSuperObjects=[SubData{prop1=4, prop2='foo'}, SubData{prop1=7, prop2='Bar'}]}]

Parse a json object with multiple sub-objects inside with GSON

i have an issue with the response of an API that i want to use, i was developing an APP that consumes this API using Retrofit 1.9.0 and GSON 2.3.1.
The Json that i want to parse is like:
{
"user1": {
"id": 1,
"name": "foo",
"address": "bar"
},
"user2":{
"id": 2,
"name": "foo",
"addres":"bar"
},
... it can be any number of users ...
"userN":{
"id": N,
"name": "foo,
"address": "bar"
}
}
So i have an POJO named User:
public class User{
private int id;
private String name;
private String address;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
I was trying to parse that json with the configuration Map<String, User>:
Callback< <Map<String, User> > callback
But when i run it, i have a GSON error:
gson expecting array but found object
How can i parse the json in a correct way?
PD: i can't change the API response, it's an external API.
Try specifying a concrete type for you callback, like HashMap. Map is an interface.
Callback< <HashMap<String, User> > callback;
Usually you will get casting exception instead of a parsing error, but worth a try.

Spring not accepting embedded json

I am stuck with a JSON and need assistance for the same.
I've a JSON like below which I'm getting from client. i'm using Chrome rest client to pass parameters.The content-type is "application/json"
My controller and bean classes are below. When I am making a post call I'm getting 400 bad request error.Please let me what is wrong? Is there any other way to implement apart from using so many bean classes?
{
"addSurveyRequest": {
"data": {
"suveyName": "abcde",
"surveyDesc": "pqrst",
"surveyFromDate": "MM-DD-YYYY",
"surveyToDate": "MM-DD-YYYY",
"surveyOptions": [
{
"surveyOptionName": "abc",
"surveyOptionDesc": "qwerty"
},
{
"surveyOptionName": "pqr",
"surveyOptionDesc": "asdfg"
}
]
}
}
}
I've my controller like
#RequestMapping(value = "/add", method = RequestMethod.POST, consumes="application/json", produces="application/json")
public #ResponseBody String addSurvey(#RequestBody AddSurveyRequest addSurveyRequest) throws Exception
{
String surveyId=null;
logger.debug("surveyName"+addSurveyRequest.getDataDTO().getSurveyDTO().getSurveyName()+"surveyDesc "+addSurveyRequest.getDataDTO().getSurveyDTO().getSurveyDesc()+"fromdate"+addSurveyRequest.getDataDTO().getSurveyDTO().getSurveyFromDate());
}
I've my bean class like below.
public class AddSurveyRequest{
private DataDTO data;
//setteres and getters
}
public class DataDTO{
private SurveyDTO surveyDTO;
//setteres and getters
}
public class SurveyDTO {
private int surveyId;
private String surveyName;
private String surveyDesc;
private Date surveyFromDate;
private Date surveyToDate;
private List<SurveyOptionDTO> surveyOptions;
//setteres and getters
}
public class SurveyOptionDTO {
private String surveyOptionName;
private String surveyOptionDesc;
//setteres and getters
}
Any help greatly appreciated. Thanks in advance!
I would say you should change the JSON input removing the outer addSurveyRequest as you declared the AddSurveyRequest type as parameter:
{
"data": {
"suveyName": "abcde",
"surveyDesc": "pqrst",
"surveyFromDate": "MM-DD-YYYY",
"surveyToDate": "MM-DD-YYYY",
"surveyOptions": [
{
"surveyOptionName": "abc",
"surveyOptionDesc": "qwerty"
},
{
"surveyOptionName": "pqr",
"surveyOptionDesc": "asdfg"
}
]
}
}
I made the variable name's in my Java class same as JSON parameter name and it worked out for me. I got this answer from another link, missed the link.
Below are the changes I made and it worked!
public class AddSurveyRequest{
private DataDTO addSurveyRequest;
//setteres and getters
}
public class DataDTO{
private SurveyDTO data;
//setteres and getters
}

Marshalling java Lists with JAXB / JSON for jqGrid using Jettison or Jackson

I am implementing a generic java POJO wrapper for jqGrid consumption, using JAXB and JSON. This is a CXF service so my marshaller libraries of choice are either Jettison or Jackson:
#XmlRootElement(name = "response")
public class JQGridResponseWrapper<T> {
private PaginatedResults<T> results;
public JQGridResponseWrapper() {
}
public JQGridResponseWrapper(PaginatedResults<T> results) {
this.results = results;
}
#XmlElementWrapper(name = "records")
#XmlElement(name = "record")
public List<T> getRecords() {
return results.getRecords();
}
#XmlElement(name = "pager")
public Pager getPager() {
return results.getPager();
}
}
Here's a sample POJO to be wraped by the generic wrapper:
#XmlRootElement
public class Note {
private Long id;
private String subject;
private String description;
private Project project;
public Note() {}
public Note(Long id, String subject, String description, Project project) {
this.id = id;
this.subject = subject;
this.description = description;
this.project = project;
}
#XmlElement(name="noteId")
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Project getProject() {
return project;
}
public void setProject(Project project) {
this.project = project;
}
}
When marshaling to XML, everything works fine, all types are correctly mapped, and I get a parent <records> element containing an array of <record> elements. But when marshaling to JSON (the project requirement), the 'record' element is unnamed, which makes jqGrid choke:
{"records":[
{"subject":"subject aaa",
"description":"Description dsifj ofdisjo",
"project":{
"projectCode":"HWIIA",
"description":"project description",
"brand":null,
"projectId":101
},
"noteId":201
},
{"subject":"subject bbb",
"description":"Description odisfj doisjf odsijf",
"project":{
"projectCode":"HWIIA",
"description":"project description",
"brand":null,
"projectId":101
},
"noteId":202
},
{"subject":"subject ccc",
"description":"Description oijgf gfoij jgifif",
"project":{
"projectCode":"HWIIA",
"description":"project description",
"brand":null,
"projectId":101
},
"noteId":203
}
],
"pager"{
"recordsPerPage":10,
"currentPage":1,
"fromRecord":1,
"toRecord":3,
"totalRecords":3,
"totalPages":1}}
I need to get a name for each record in the records array. Is there a simple way to make this work, either with Jettion or Jackson? I searched and searched the web but couldn't find a straighforward solution for my target marshaler libraries. I did see some answers for MOXY, but it is problematic for me to change libraries at this point. Any help greatly appreciated.