I need to receive a list of JSON entity as input to my WS.
Here my entity definition:
#XmlRootElement
public class ContactClass {
public String action;
public Long userId;
public String phone;
public String fbId;
}
here my WS function definition:
#PUT
#Path("/{userId}/adBook")
public String synchAdBookContacts(#PathParam("userId") Long userId, ArrayList<ContactClass> contacts)
Removing ArrayList<> It works fine, but I need an array of ContactClass as input.
Can you help me please?
Thank you!
Update:
Finally I found the solution, here the article that have solved my issue:
https://blogs.oracle.com/japod/entry/missing_brackets_at_json_one
Bean 1:
#XmlRootElement
public class Contact {
private String name;
private String phoneNumber;
// Getters, setters, default constructor
}
Bean 2:
#XmlRootElement
public class Contacts {
private List<Contact> contacts;
//Getter for contacts
#XMLElement(name = "listContacts")
public List<Contact> getContacts() {
....
// Getters, setters, default constructor
}
You Json fiel should have the following format:
"listContacts":[{"json for contact1"},{"json for contact2"},{"json for contact3"}...]
Your Resource:
#PUT
#Path("/{userId}/adBook")
public String synchAdBookContacts(#PathParam("userId") Long userId, Contacts contacts) {
//Here you can get your contacts contacts.
Deserializing to a list should work just fine. The following code works with RESTeasy + Jackson:
Bean:
#XmlRootElement
public class Contact implements Serializable {
private static final long serialVersionUID = 2075967128376374506L;
private String name;
private String phoneNumber;
// Getters, setters, default constructor
}
Resource:
#Path("/othertest")
public class AnotherTestResource {
#POST
#Path("/list/{id}")
#Produces("application/json")
#Consumes("application/json")
public Response requestWithList(#PathParam("id") String id,
List<Contact> contacts) {
return Response.ok("Hello World: " + contacts.size()).build();
}
}
Annotating your synchAdBookContacts method with #Consumes("application/json") should do it. Which JAX-RS implementation are you using and what error are you exactly getting?
Related
I'm learning the Spring Framework and I'm struggling with the Rest services with spring, in particular for the POST call that it's supposed to add a new object to the database.
I've seen a lot of different implementations through the web, but I don't know how to pick the best.
Let's take for example a film class:
#Entity
public class Film {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String title;
private String description;
//Constructor, Getter and Setter Omitted.
}
Assuming the repository extends the JpaRepository<Film,Long>, this would be the Controller class:
#RestController
public class FilmController {
#Autowired
FilmRepository filmRepository;
//Implementation #1
#PostMapping("/film")
public Film addNew(#RequestBody Map<String,String> body){
String title = body.get("title");
String description = body.get("description");
return filmRepository.save(new Film(title,description));
}
//Implementation #2
#PostMapping("/film")
public Film addNew(String title, String description){
Film film = new Film(title,description);
System.out.println(film.getTitle() + " " + film.getDescription());
return filmRepository.save(film);
}
//Implementation #3
#PostMapping("/film")
public Film addNew(#RequestBody Film newFilm){
return filmRepository.save(newFilm);
}
}
Why some implementations have as parameter a Map<String, String> ? Is that a body mapped to a key/value pair ?
Also bear in mind that I managed to implement correctly just the implementation #2, the first and the third gave me a
415 error:"Unsupported Media Type" org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'multipart/form-data;boundary=--------------------------901298977805450214809889;charset=UTF-8' not supported]
(Despite I followed the official Spring tutorial) on REST services.
I also read something about the creation of DTO classes where I can define attributes without exposing the object to the controller, how can be implemented such solution?
Implementation 3 is the best practice, but you should create a lightweight DTO class (maybe FilmDto) to avoid exposing the internal structure of your entity, please see LocalDTO, Martin Fowler.
You may use ModelMapper to map FilmDto to Film, and make sure there are proper getters and setters in both classes, if the getters and setters have the same names in both classes, then ModelMapper will do the conversion smoothly:
public class FilmDto {
private long id;
private String title;
private String description;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
and you controller:
#RestController
#RequestMapping("/api")
public class FilmController {
private final FilmRepository filmRepository;
private ModelMapper modelMapper = new ModelMapper();
#Autowired
public FilmController(FilmRepository filmRepository) {
this.filmRepository = filmRepository;
}
//Implementation #3
#PostMapping("/film")
public ResponseEntity<FilmDto> addNew(#RequestBody FilmDto filmDto){
Film newFilm = modelMapper.map(filmDto, Film.class);
newFilm = filmRepository.save(newFilm);
filmDto.setId(newFilm.getId());//you may use modelMapper here
return ResponseEntity.ok(filmDto);
}
}
you can test using postman by passing the film as below:
{
"title": "some title",
"description": "some description"
}
and the body should be of type "raw", "JSON".
Why some implementations have as parameter a Map<String, String> ?
some implementations use map<key,value> because they need the properties that map interface provide such as non-duplicate key value or the classes that implement map interface such as TreeMap and LinkedHashMap.
about your implementation of the class FilmController i think its not necessary to use map<String,String> for posting your domain in the data base simply you can have this implementation
#RestController
public class FilmController {
#Autowired
FilmRepository filmRepository;
#PostMapping("/film")
public ResponseEntity addNew(#RequestBody Film film){
return ResponseEntity.ok(filmRepository.save(film));
What is the best solution for creating POJO, at controller level or method level.
For example I have EmployeeController which contains below methods.
getAllEmployees()
addEmployee(AddEmployeeRequest employee)
updateEmployee(UpdateEmployeeRequest employee)
removeEmployee(RemoveEmployeeRequest employee)
//Method level classes
public class AddEmployeeRequest
{
private String name;
private Date dateOfBirth;
private String Address;
}
public class UpdateEmployeeRequest
{
private long id;
private String Address;
}
public class RemoveEmployeeRequest
{
private long id;
}
or
getAllEmployees()
addEmployee(EmployeeRequest employee)
updateEmployee(EmployeeRequest employee)
removeEmployee(EmployeeRequest employee)
//Controller level class
public class EmployeeRequest
{
private long id;
private String name;
private Date dateOfBirth;
private String Address;
}
If I have method level models then do I have to create the respective sevice level DTO models also ?
In fact, if you are using spring, it is neccessary to use a single POJO because spring use reflection for accessing the ClassName, DeclaredFields etc. Using multiple POJO will be annoying for Spring.
See here some more details about reflection: https://crunchify.com/create-simple-pojo-and-multiple-java-reflection-examples/
Is it possible to create a GET webservice in spring and using nested properties in the query? Like search.limitResults in the following example:
localhost:8080/firstname=test&search.limitResults=10
You get the idea. Can this be achieved?
#RestController
public class MyServlet {
#RequestMapping(value = "/", method = RequestMethod.GET)
private String test(RestParams p) {
}
}
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class RestParams {
private String firstname;
private String lastname;
//is that possible to nest?
private Search search;
}
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Search {
private int limitResults;
//some more
}
To answer my own question: it just works this way! Nested properties can be accessed using the dot accessor, like search.limitResults.
I didn't have this problem before, with other POJOs, I'm not sure what's different this time, but I can't get this working and I could not find an exact solution for this.
I have this POJO called Component (with some Hibernate annotations):
#Entity
#Table(name="component", uniqueConstraints={#UniqueConstraint(
columnNames = {"name", "component_type"})})
public class Component {
#Column(name="id")
#Id #GeneratedValue(strategy=GenerationType.AUTO)
private int id;
#Column(name="name")
private String name;
#Column(name="component_type")
private String componentType;
#Column(name="serial_number")
private int serialNumber;
#Column(name="active_since")
private String activeSince;
#Embedded
private ComponentWearoutModel wearout;
public Component() {
}
public Component(String name, String componentType, int serialNumber, String activeSince,
ComponentWearoutModel wearout) {
this.name = name;
this.componentType = componentType;
this.serialNumber = serialNumber;
this.activeSince = activeSince;
this.wearout = wearout;
}
public ComponentWearoutModel getModel() {
return wearout;
}
public void setModel(ComponentWearoutModel wearout) {
this.wearout = wearout;
}
//more getters and setters
}
ComponentWearoutModel:
#Embeddable
public class ComponentWearoutModel {
private String componentType; //dont mind the stupid duplicate attribute
private Integer componentLifeExpectancy;
private Float componentWearOutLevel;
private Float actionThreshold;
public ComponentWearoutModel() {
}
public ComponentWearoutModel(String componentType, int componentLifeExpectancy, float componentWearOutLevel,
float actionThreshold) {
this.componentType = componentType;
this.componentLifeExpectancy = componentLifeExpectancy;
this.componentWearOutLevel = componentWearOutLevel;
this.actionThreshold = actionThreshold;
}
//getters and setters
}
The sample payload I use:
{
"name": "component name",
"componentType": "airfilter2",
"serialNumber": 573224,
"activeSince": "2016-04-10 17:38:41",
"wearout":
{
"componentType": "airfilter",
"componentLifeExpectancy": 1000,
"componentWearOutLevel": 0.24,
"actionThreshold": 0.2
}
}
And finally the resource class:
#Path("myresource")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
public class MyResource {
DatabaseManager dm = DatabaseManager.getInstance();
#PUT
#Path("Component")
public Response storeComponent(Component component){
System.out.println("reached");
System.out.println(component.getComponentType()); //okay
System.out.println(component.getModel().getComponentType()); //nullpointerexception
ComponentWearoutModel model = new ComponentWearoutModel("type", 1000, 1f, 0.2f);
component.setModel(model); //this way it's saved in the db just fine
dm.save(component);
return Response.status(Status.OK).entity(component).build();
}
}
Without the prints, only the fields which are not part of the ComponentWearoutModel class are stored in the database table, the other columns are null. So when I try to print one of them, I get an exception, I just dont understand why. If I create a ComponentWearoutModel in the resource method and add it to the component, everything is fine in the database.
UPDATE:
so my mistake was that I named the ComponentWearoutModel attribute as "wearout" in the Component.class, but the autogenerated getters and setter were called getModel/setModel and moxy could not parse my payload because of this. Solution: change the attribute name to "model" in Component class and in payload too.
Please ensure that the attribute names you are using in the POJO are same as what are being sent in the json string.
Since there are no jackson etc annotations being used in your POJO to tell it the corresponding json mapping, the underlying code will directly use the names given in json string. If you are using the string "model", the convertor code will look for a "setModel" method in your POJO.
In the above example, either call everything "model", or "wearable".
I have the following classes:
public class Student {
private Long id ;
private String firstName;
private String lastName;
private Set<Enrollment> enroll = new HashSet<Enrollment>();
//Setters and getters
}
public class Enrollment {
private Student student;
private Course course;
Long enrollId;
//Setters and Getters
}
I have Struts2 controller and I would like to to return Serialized instance of Class Student only.
#ParentPackage("json-default")
public class JsonAction extends ActionSupport{
private Student student;
#Autowired
DbService dbService;
public String populate(){
return "populate";
}
#Action(value="/getJson", results = {
#Result(name="success", type="json")})
public String test(){
student = dbService.getSudent(new Long(1));
return "success";
}
#JSON(name="student")
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
}
It returns me the serializable student object with all sub classes, but I would like to have only student object without the hashset returned .
How can I tell Struts to serialize only the object?
I do have Lazy loading enabled and hashset is returned as proxy class.
See the answer here which shows the use of include and exclude properties. I don't think the example clearly shows excluding nested objects however I have used it for this purpose. If you still have issues I'll post a regex which will demonstrate this.
Problem with Json plugin in Struts 2
Edit:
Here is an example of using exclude properties in an annotation which blocks the serialization of a nested member:
#ParentPackage("json-default")
#Result(type = "json", params = {
"excludeProperties",
"^inventoryHistory\\[\\d+\\]\\.intrnmst, selectedTransactionNames, transactionNames"
})
public class InventoryHistoryAction extends ActionSupport {
...
inventoryHistory is of type InventoryHistory a JPA entity object, intrnmst references another table but because of lazy loading if it were serialized it would cause an Exception when the action is JSON serialized for this reason the exclude parameter has been added to prevent this.
Note that
\\
is required for each \ character, so a single \ would only be used in the xml where two are required because of escaping for the string to be parsed right.
#Controller
#Results({
#Result(name="json",type="json"
, params={"root","outDataMap","excludeNullProperties","true"
,"excludeProperties","^ret\\[\\d+\\]\\.city\\.province,^ret\\[\\d+\\]\\.enterprise\\.userinfos","enableGZIP","true"
})
})
public class UserinfoAction extends BaseAction {
#Action(value="login")
public String login(){
if(jsonQueryParam!=null && jsonQueryParam.length()>0)
{
user = JsonMapper.fromJson(jsonQueryParam, TUserinfo.class);
}
Assert.notNull(user);
//RESULT="ret" addOutJsonData: put List<TUserinfo> into outDataMap with key RESULT for struts2 JSONResult
addOutJsonData(RESULT, service.login(user));
return JSON;
}
public class TUserinfo implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private String userid;
private String username;
private String userpwd;
private TEnterpriseinfo enterprise;
private String telphone;
private TCity city;
......
}
public class TEnterpriseinfo implements java.io.Serializable {
private String enterpriseid;
private String enterprisename;
private Set<TUserinfo> userinfos = new HashSet<TUserinfo>(0);
.......}
before set the excludeProperties property,the result is below:
{"ret":[
{
"city":{"cityename":"tianjin","cityid":"12","cityname":"天津"
,"province": {"provinceename":"tianjing","provinceid":"02","provincename":"天津"}
}
,"createddate":"2014-01-07T11:13:58"
,"enterprise":{"createddate":"2014-01-07T08:38:00","enterpriseid":"402880a5436a227501436a2277140000","enterprisename":"测试企业2","enterprisestate":0
,"userinfos":[null,{"city":{"cityename":"beijing","cityid":"11","cityname":"北京","province":{"provinceename":"beijing","provinceid":"01","provincename":"北京市"}
},"comments":"ceshi","createddate":"2004-05-07T21:23:44","enterprise":null,"lastlogindate":"2014-01-08T08:50:34","logincount":11,"telphone":"2","userid":"402880a5436a215101436a2156e10000","username":"0.5833032879881197","userpwd":"12","userstate":1,"usertype":0}]
}
,"lastlogindate":"2014-01-08T10:32:43","logincount":0,"telphone":"2","userid":"402880a5436ab13701436ab1b74a0000","username":"testUser","userpwd":"333","userstate":1,"usertype":0}]
}
after set the excludeProperties property,there are not exist province and userinfos nodes, the result is below:
{"ret":
[{
"city":{"cityename":"tianjin","cityid":"12","cityname":"天津"}
,"createddate":"2014-01-07T11:13:58"
,"enterprise":{"createddate":"2014-01-07T08:38:00","enterpriseid":"402880a5436a227501436a2277140000","enterprisename":"测试企业2","enterprisestate":0}
,"lastlogindate":"2014-01-08T11:05:32","logincount":0,"telphone":"2","userid":"402880a5436ab13701436ab1b74a0000","username":"testUser","userpwd":"333","userstate":1,"usertype":0
}]
}