#RequestMapping(value = "/action")
public MyObject myAction(
#RequestParam(value = "prop1", required = false) String prop1,
#RequestParam(value = "prop2", required = false) String prop2,
#RequestParam(value = "prop3", required = false) String prop3) { ... }
And I have this instead many parameter and it's working:
public class MyObject {
private String prop1;
private String prop2;
private String prop3;
//Getters and setters
...
}
#RequestMapping(value = "/action")
public MyObject myAction(MyObject myObject)
But I have problem when I am trying avoid duplicate
public class MyClass {
private MyObject param;
//Getters and setters
...
}
#RequestMapping(value = "/action")
public MyObject myAction(MyClass myClass)
What should my url with parameters look like now?
param={prop1=a1&prop2=a2&prop3=a3&} ?
Actually requesting in same way works
#RestController
public class HelloController {
#RequestMapping(value = "/action")
public Person myAction(Person person) {
return person;
}
}
public class Person {
private String name;
private String address;
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;
}
}
URL Request:
http://localhost:8080/action?name=George&address=UK
Response
{
"name":"George",
"address":"UK"
}
Related
I want to convert an old application to a new one in which i make HTTP calls (GET/POST/DELETE). I have a model package where i have my POJO classes and i made a DTO package where i have Dto classes. When i make a simple GET method to take a String message as a response i dont have any problem but when i use dependency injection and i want to use GET or POST methods to see some details about my app i get in postman this message: inStream parameter is null
OwnerResource.java
#Path("ownerResource")
public class OwnerResource {
#GET
public Response ping() { // When i use only this method i dont have any problem and i can see the message
return Response
.ok("pang")
.build();
}
// When i use the below code i have problem in HTTP calls
#Inject
private OwnerService ownerService;
#GET
#Path("owner/{ownerId}")
#Produces("application/json")
public RestApiResult<PropertyOwnerDto> getOwner(#PathParam("ownerId") int ownerId) {
return ownerService.getOwner(ownerId);
}
#POST
#Path("owner")
#Produces("application/json")
#Consumes("application/json")
public void createNewOwner(PropertyOwnerDto owner) {
ownerService.createPropertyOwner(owner);
}
}
PropertyOwner.java
#Data
#NoArgsConstructor
#Entity(name = "propertyowner")
#Table
public class PropertyOwner extends PersistentClass {
// id is in PersistenceClass
private int vat;
private String name;
private String surname;
private String address;
private String phoneNumber;
private String email;
private String username;
private String password;
#OneToMany(mappedBy = "owner", orphanRemoval = true)
private List<Property> properties;
public PropertyOwner(int vat, String name, String surname, String address, String phoneNumber, String email, String username, String password) {
this.vat = vat;
this.name = name;
this.surname = surname;
this.address = address;
this.phoneNumber = phoneNumber;
this.email = email;
this.username = username;
this.password = password;
}
}
PropertyOwnerDto.java
#Data#Getter#Setter
public class PropertyOwnerDto {
private int vat;
private String name;
private String surname;
private String address;
private String phoneNumber;
private String email;
private String username;
private String password;
public PropertyOwnerDto() {
}
public PropertyOwnerDto(PropertyOwner propertyOwner) {
this.vat = propertyOwner.getVat();
this.name = propertyOwner.getName();
this.surname = propertyOwner.getSurname();
this.address = propertyOwner.getAddress();
this.phoneNumber = propertyOwner.getPhoneNumber();
this.email = propertyOwner.getEmail();
this.username = propertyOwner.getUsername();
this.password = propertyOwner.getPassword();
}
public PropertyOwner asOwner() {
PropertyOwner propertyOwner = new PropertyOwner();
propertyOwner.setVat(vat);
propertyOwner.setName(name);
propertyOwner.setSurname(surname);
propertyOwner.setAddress(address);
propertyOwner.setPhoneNumber(phoneNumber);
propertyOwner.setEmail(email);
propertyOwner.setUsername(username);
propertyOwner.setPassword(password);
return propertyOwner;
}
OwnerService.java
public interface OwnerService {
void createPropertyOwner(PropertyOwnerDto ownerDto);
RestApiResult<PropertyOwnerDto> getOwner(int ownerId);
}
OwnerServiceImpl.java
public class OwnerServiceImpl implements OwnerService {
private final Properties sqlCommands = new Properties();
{
final ClassLoader loader = getClass().getClassLoader();
try ( InputStream config = loader.getResourceAsStream("sql.properties")) {
sqlCommands.load(config);
} catch (IOException e) {
throw new IOError(e);
}
}
private static final Logger logger = LogManager.getLogger(OwnerServiceImpl.class);
#Inject
private PropertyOwnerRepository propertyOwnerRepository;
#Inject
private PropertyRepository propertyRepository;
#Inject
private RepairRepository repairRepository;
// SOME OTHER CODE HERE //
#Override
public RestApiResult<PropertyOwnerDto> getOwner(int ownerId) {
PropertyOwnerDto ownerDto = new PropertyOwnerDto(propertyOwnerRepository.findById(ownerId));
return new RestApiResult<PropertyOwnerDto>(ownerDto, 0, "successful");
}
#Override
public void registerNewPropertyDto(PropertyDto propertyDto) {
propertyRepository.create(propertyDto.asProperty());
}
}
beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd"
bean-discovery-mode="all">
</beans>
I use WILDFLY as application server.
I am trying to merge JSON objects with the new #JsonMerge annotation. I found a sample online that works when I run it in my IDE. Here's a snippet to run:
#Test
void mergeTest() throws IOException {
final Employee employee = new Employee("Serializon", new Address("Street 1", "City 1", "ZipCode1"));
final Employee newEmployee = new Employee("Serializon", new Address("Street 2", "City 2", "ZipCode2"));
assertThat(employee.getAddress().getCity()).isEqualTo("City 1");
final ObjectMapper objectMapper = new ObjectMapper();
final Employee mergedEmployee = objectMapper.readerForUpdating(employee).readValue(JSONUtil.toJSON(newEmployee));
System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(mergedEmployee));
assertThat(newEmployee.getAddress().getCity()).isEqualTo("City 2");
assertThat(mergedEmployee.getAddress().getCity()).isEqualTo("City 2");
}
public class Employee {
private String name;
#JsonMerge
private Address address;
public Employee(final String name, final Address address) {
this.name = name;
this.address = address;
}
public String getName() {
return name;
}
public Address getAddress() {
return address;
}
}
public class Address {
private String street;
private String city;
private String zipCode;
public Address(final String street, final String city, final String zipCode) {
this.street = street;
this.city = city;
this.zipCode = zipCode;
}
public String getStreet() {
return street;
}
public String getCity() {
return city;
}
public String getZipCode() {
return zipCode;
}
}
When I try to reproduce this with my own class, it fails with the following error:
Deserialization of [simple type, class package.State] by passing existing instance (of package.State) not supported
My class in question is a POJO with some lists and primitive properties, all with getters. It is constructed using a builder and is immutable. It looks like this:
#JsonDeserialize(builder = State.Builder.class)
public class State {
private final String id;
#JsonMerge
private final List<Module> modules;
protected State(final Builder builder) {
this.id = builder.id;
this.modules = builder.modules;
}
public static Builder builder() {
return new Builder();
}
public String getId() {
return id;
}
public List<Module> getModules() {
return modules;
}
#JsonIgnoreProperties(ignoreUnknown = true)
public static final class Builder {
private String id;
private List<Module> modules;
private Builder() {
}
public Builder withId(final String id) {
this.id = id;
return this;
}
public Builder withModules(final List<Module> modules) {
this.modules = modules;
return this;
}
public State build() {
return new State(this);
}
}
}
The merge annotation states the following:
Merging is only option if there is a way to introspect current state: if there is accessor (getter, field) to use. Merging can not be enabled if no accessor exists or if assignment occurs using a Creator setter (constructor or factory method), since there is no instance with state to introspect.
So I thought perhaps the builder might the problem, but retrofitting the Employee/Address sample with a builder still works:
#Test
void mergeTest() throws IOException {
final Employee employee = Employee.newBuilder()
.withName("Serializon")
.withAddress(Address.newBuilder()
.withStreet("Steet 1")
.withCity("City 1")
.withZipCode("ZipCode1")
.build())
.build();
assertThat(employee.getAddress().getCity()).isEqualTo("City 1");
final Employee newEmployee = Employee.newBuilder()
.withName("Serializon")
.withAddress(Address.newBuilder()
.withStreet("Steet 2")
.withCity("City 2")
.withZipCode("ZipCode2")
.build())
.build();
final ObjectMapper objectMapper = new ObjectMapper();
final Employee mergedEmployee = objectMapper.readerForUpdating(employee).readValue(JSONUtil.toJSON(newEmployee));
System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(mergedEmployee));
assertThat(newEmployee.getAddress().getCity()).isEqualTo("City 2");
assertThat(mergedEmployee.getAddress().getCity()).isEqualTo("City 2");
}
#JsonDeserialize(builder = Employee.Builder.class)
public class Employee {
private String name;
#JsonMerge
private Address address;
private Employee(final Builder builder) {
name = builder.name;
address = builder.address;
}
public static Builder newBuilder() {
return new Builder();
}
public String getName() {
return name;
}
public Address getAddress() {
return address;
}
public static final class Builder {
private String name;
private Address address;
private Builder() {
}
public Builder withName(final String name) {
this.name = name;
return this;
}
public Builder withAddress(final Address address) {
this.address = address;
return this;
}
public Employee build() {
return new Employee(this);
}
}
}
#JsonDeserialize(builder = Address.Builder.class)
public class Address {
private String street;
private String city;
private String zipCode;
private Address(final Builder builder) {
street = builder.street;
city = builder.city;
zipCode = builder.zipCode;
}
public static Builder newBuilder() {
return new Builder();
}
public String getStreet() {
return street;
}
public String getCity() {
return city;
}
public String getZipCode() {
return zipCode;
}
public static final class Builder {
private String street;
private String city;
private String zipCode;
private Builder() {
}
public Builder withStreet(final String street) {
this.street = street;
return this;
}
public Builder withCity(final String city) {
this.city = city;
return this;
}
public Builder withZipCode(final String zipCode) {
this.zipCode = zipCode;
return this;
}
public Address build() {
return new Address(this);
}
}
}
Finally I tried to have a list of addresses instead, and accepting the list in the builder as withAddresses instead. So, for brevity:
#JsonDeserialize(builder = Employee.Builder.class)
public class Employee {
#JsonMerge
private List<Address> addresses;
public static final class Builder {
public Builder withAddresses(final List<Address> addresses) {
this.addresses = addresses;
return this;
}
}
}
And when I run the testcase again, this fails with the same error as my own code:
Deserialization of [simple type, class se.itab.locker.core.util.Employee] by passing existing instance (of se.itab.locker.core.util.Employee) not supported
What is actually going on here, and can I resolve it somehow or is this an unsupported use case or bug?
Update
So I found that this works:
//#JsonDeserialize(builder = Employee.Builder.class)
public class Employee {
#JsonCreator
public Employee(final Employee employee) {
name = employee.name;
addresses = employee.addresses;
stringAddresses = employee.stringAddresses;
}
But then serializing causes an infinite loop instead.
I am trying to get the following Json into POJOS using #RequestBody Instance instance
{
"service_id": "service-id-here",
"plan_id": "plan-id-here",
"context": {
"platform": "cloudfoundry",
"some_field": "some-contextual-data"
},
"organization_guid": "org-guid-here",
"space_guid": "space-guid-here",
"parameters": {
"agent_name": 1,
"url": "foo",
"credential": "asdasd",
"ia_url": "asdasd"
}
}
Below are my POJOs
Instance
public class Instance {
#JsonProperty(value = "service_id")
String serviceId;
#JsonProperty(value = "plan_id")
String planId;
//TODO : Replace with Context class when the spec defines things clearly
#JsonProperty(value = "context")
Object context;
#JsonProperty(value = "organization_guid")
String organizationGuid;
#JsonProperty(value = "space_guid")
String spaceGuid;
#JsonProperty(value = "parameters")
Parameters parameters;
}
Parameters
public class Parameters {
#JsonProperty(value = "agent_name")
String agentName;
#JsonProperty(value = "url")
String url;
#JsonProperty(value = "credential")
String credential;
#JsonProperty(value = "ia_url")
String iaUrl;
}
I use #JsonProperty everywhere. Is there any way to get underscore separated json keys into java's naming convention for variables (Camelcase)??
I tried using #JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) to my POJO classes instead of the #JsonProperty for each parameter. I just get an empty json {} in instance. What am I missing here?
Yes, is this possible using PropertyNamingStrategy class through JsonNaming annotation
Ex:
#JsonNaming(PropertyNamingStartergy.LowerCaseWithUnderscoresStrategy.class)
class Class_name{
...
}
//----
The below code has updated. In that code am using
PropertyNamingStrategy.SnakeCaseStrategy
Working code (TESTED).
Getters and setters are important for this to work. But #JsonProperty does not require them
User.java
#JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class User {
private int id;
private String beanName;
private Role role;
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getBeanName() {
return beanName;
}
public void setBeanName(String beanName) {
this.beanName = beanName;
}
}
Role.java
#JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class Role {
private int id;
private String roleName;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
}
Here is the Controller
#RestController
#RequestMapping("/test")
public class NamingController {
#RequestMapping(value="/jsontopojo", method = RequestMethod.POST)
public ResponseEntity<User> jsontopojo(#RequestBody User nam) {
return new ResponseEntity<User>( nam, HttpStatus.OK);
}
}
Whenever I call save() method the same ID is shared between three different entities and I don't know why ?
#Entity
public class Department {
#Id
#GeneratedValue
private Long departmentId;
private String name;
public Department(Long departmentId) {
this.departmentId = departmentId;
}
public Department() {
}
public Department(String name) {
this.name = name;
}
public Long getDepartmentId() {
return departmentId;
}
public void setDepartmentId(Long departmentId) {
this.departmentId = departmentId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
#Entity
public class Location {
#Id
#GeneratedValue
private Long locationId;
private String name;
public Location(Long locationId) {
this.locationId = locationId;
}
public Location() {
}
public Location(String name) {
this.name = name;
}
public Long getLocationId() {
return locationId;
}
public void setLocationId(Long locationId) {
this.locationId = locationId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
And this is my Controller:
#RestController
public class SettingsController {
#Autowired
private LocationRepository locationRepository;
#Autowired
private DepartmentRepository departmentRepository;
#Autowired
private RoleRepository roleRepository;
#RequestMapping(value = "/api/locations", method = RequestMethod.POST)
public ResponseEntity addLocation(#RequestBody DataForm dataForm) {
if (dataForm == null) {
return new ResponseEntity(HttpStatus.BAD_REQUEST);
}
locationRepository.save(new Location(dataForm.getName()));
return new ResponseEntity(HttpStatus.CREATED);
}
#RequestMapping(value = "/api/roles", method = RequestMethod.POST)
public ResponseEntity addRole(#RequestBody DataForm dataForm) {
if (dataForm == null) {
return new ResponseEntity(HttpStatus.BAD_REQUEST);
}
roleRepository.save(new Role(dataForm.getName()));
return new ResponseEntity(HttpStatus.CREATED);
}
#RequestMapping(value = "/api/departments", method = RequestMethod.POST)
public ResponseEntity addDepartment(#RequestBody DataForm dataForm) {
if (dataForm == null) {
return new ResponseEntity(HttpStatus.BAD_REQUEST);
}
departmentRepository.save(new Department(dataForm.getName()));
return new ResponseEntity(HttpStatus.CREATED);
}
}
This should happen only if the id would be static, but It's not. If I create two new Location() objects, when I will create a new Department() the Id of the department will be 3. Why ?
Since you didn't specify the strategy for #GeneratedValue, I guess that Hibernate uses the same sequence for all your entities.
You can set something like
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="department_seq_gen")
#SequenceGenerator(name="department_seq_gen", sequenceName="DEPARTMENT_SEQ")
on Department entity, and something similar on Location entity (just use location_seq_gen and LOCATION_SEQ).
I am implementing a sample Spring MVC Form with Form Validation. I have a complex type Address as bean property for Student form bean. And I have added form validation #NotEmpty for Address bean properties. But the same is not reflecting in the UI. But form validation works for other primitive types of Student form bean.
So, Validation works perfectly for Student form bean but not for nested complex types like Address within Student form bean.
I am trying understand the reason and a fix.
Spring version 4.0+.
Hibernate Validator api:5.2.4
Student POJO:
package com.xyz.form.beans;
import java.util.Date;
import java.util.List;
import javax.validation.constraints.Past;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.NotEmpty;
import com.xyz.validators.DateNotEmpty;
import com.xyz.validators.ListNotEmpty;
public class Student {
#Size(min = 2, max = 30)
private String firstName;
#Size(min = 2, max = 30)
private String lastName;
#NotEmpty
private String gender;
#DateNotEmpty
#Past
private Date DOB;
private String email;
private String mobileNumber;
#ListNotEmpty
private List<String> courses;
private Address address;
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Date getDOB() {
return DOB;
}
public void setDOB(Date dOB) {
DOB = dOB;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getMobileNumber() {
return mobileNumber;
}
public void setMobileNumber(String mobileNumber) {
this.mobileNumber = mobileNumber;
}
public List<String> getCourses() {
return courses;
}
public void setCourses(List<String> courses) {
this.courses = courses;
}
}
Address POJO:
package com.xyz.form.beans;
import org.hibernate.validator.constraints.NotEmpty;
import com.xyz.validators.LongNotEmpty;
public class Address {
#NotEmpty
private String houseNo;
#NotEmpty
private String street;
#NotEmpty
private String area;
#NotEmpty
private String city;
#LongNotEmpty
private Long pin;
public String getHouseNo() {
return houseNo;
}
public void setHouseNo(String houseNo) {
this.houseNo = houseNo;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getArea() {
return area;
}
public void setArea(String area) {
this.area = area;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public Long getPin() {
return pin;
}
public void setPin(Long pin) {
this.pin = pin;
}
}
Student Controller:
#RequestMapping(value = "/newStudentDetails.do", method = RequestMethod.POST)
public ModelAndView newStudentDetails(
#Valid #ModelAttribute("student") com.xyz.form.beans.Student studentFormBean,
BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return new ModelAndView("newStudentPage");
}
Student studentDto = new Student();
studentDto.setFirstName(studentFormBean.getFirstName());
studentDto.setLastName(studentFormBean.getLastName());
studentDto.setGender(studentFormBean.getGender());
studentDto.setDOB(new Date(studentFormBean.getDOB().getTime()));
studentDto.setEmail(studentFormBean.getEmail());
studentDto.setMobileNumber(studentFormBean.getMobileNumber());
StringBuilder sb = new StringBuilder();
sb.append(studentFormBean.getAddress().getHouseNo() + ", ");
sb.append(studentFormBean.getAddress().getStreet() + ", ");
sb.append(studentFormBean.getAddress().getArea() + ", ");
sb.append(studentFormBean.getAddress().getCity() + "-");
sb.append(studentFormBean.getAddress().getPin());
studentDto.setAddress(sb.toString());
studentDto.setCourses(studentFormBean.getCourses());
studentDao.createStudent(studentDto);
ModelAndView mav = new ModelAndView("newStudentSuccess");
return mav;
}
Thanks,
Viswanath
You need to annotate your complex types with #Valid.
This is the reference (which references here)
Hi lets try #ModelAttribute("student") #Valid com.xyz.form.beans.Student studentFormBean in place of #Valid #ModelAttribute("student")
For nested complex types, you have to activate the direct field access. Just like below:
#org.springframework.web.bind.annotation.ControllerAdvice
public class ControllerAdvice {
#InitBinder
public void initBinder(WebDataBinder webDataBinder) {
webDataBinder.initDirectFieldAccess();
}