I am using spring 4 REST.
I have a base class and many other class extends the same.
For example, Employee is the base class and other classes hr, engineer, trainer etc etc extends the employee.
I have to create REST API to create the different type of employee.
The interface is one POST which accepts all the type of employees. I cannot create different interface for each sub type. From the base, I know what is the sub type.
#RequestMapping(value= "/test/{employeeType}", method = RequestMethod.POST)
public void createEmp(#RequestBody Employee employee){
//If type is HR, I want to cast to HR type
//Is there any way we can take generic object in spring rest and then manage internally ?
}
Maybe try this?
#RequestMapping(value= "/test/{employeeType}", method = RequestMethod.POST)
public void createEmp(#PathVariable String employeeType, #RequestBody EmployeeDTO employeeDTO){
transform(employeeType,employeeDTO);
}
Here EmployeeDTO will contain all possible parameters so it can construct any of the child classes then based on the employeeType you just transform into domain object(Employee)?
Edit2 as requested
Here is sample code:
public class Employee {
private String name;
}
public class Hr extends Employee {
private String department;
}
public class Hr extends Employee {
private String department;
}
Then the DTO class should look like this:
public class EmployeeDTO {
private String name;
private String course;
private String department;
}
Then when you know your type you transform to whatever your want with all the necessary values in the DTO
Edit: Now when I think about it this also may be an option but I will need to see your classes.
#RequestMapping(value= "/test/employee", method = RequestMethod.POST)
public void createEmp(#RequestBody Employee employee){
#RequestMapping(value= "/test/hr", method = RequestMethod.POST)
public void createHr(#RequestBody Hr hr){
Related
How can I fetch custId from a list which has a generic interface? I tried fetching custId via list.getClass.getField("custId") but unfortunately, the value is not coming.
class Emp implements CommonDto {
private String CustId;
private String EmpId;
private String EmpName;
}
class Student implements CommonDto{
private String CustId;
private String StudentId;
private String StudentName;
}
public interface CommonDto {
}
public class TestApplication {
public static void main(String[] args) throws NoSuchFieldException, IllegalArgumentException {
Emp emp = new Emp(1 , 100, "ayush");
List<Emp> temp = new ArrayList<>() ;
temp.add(emp);
List<CommonDto> commonList = new ArrayList<>();
commonList.add(temp);
for(CommonDto comm : commonList)// is this the right way to fetch CustId
{
// fetch cusId from every domain and add it to new list and return that list
}
}}
In order to access the CustId field you must first recognize that that field does not belong to the CommonDTO class. Therefore, there is no guarantee that a given instance will have that field. If every instance is supposed to have the field then move that field to that class to properly model the data.
Given the existing models, there is a CustId field in both Emp and Student but these are NOT the same field. You can test the data type of a given instance, cast it, and call the appropriate method.
public String getCustId(CommonDTO dto) {
if (dto instanceof Emp) {
return ((Emp)dto).getCustId();
}
if (dto instanceof Student) {
return ((Student)dto).getCustId();
}
return null;
}
If you have leeway to alter the data model then you can move CustId to the parent class (i.e. CommonDTO). Let's assume that his class is provided and you cannot change it. However, you still have leeway to alter the Emp and Student models.
You could also define a common interface that defines the property via method and have each of the classes implement that interface.
public interface Custmor {
public String getCustId();
}
public class Emp implements Customer {
private String custId;
public String getCustId() { return custId; }
...
}
public class Student implements Customer {
private String custId;
public String getCustId() { return custId; }
...
}
Then you can handle the data via the interface.
public String getCustId(CommonDTO dto) {
if (dto instanceof Customer) {
return ((Customer)dto).getCustId();
}
return null;
}
Alternatively, you could create a Customer class that extends the CommonDTO and serves as a base class for both of these...
public class Customer extends CommonDTO {
private custId;
public String getCustId() { return custId; }
...
}
And alter the Emp and Student classes to extend this...
public class Emp extends Customer { ... }
public class Student extends Customer { ... }
In either case (interface or common base class), you can then use instanceof to determine if a given CommonDTO instance is a Customer and cast it to access the method. You can use the Stream API to easily pull the custIds from the Customers in a List<CommonDTO>:
List<String> customerIds = commonList.stream()
.filter(c -> c instanceof Customer) // filter out the non-Customer elements out of the streawm
.map(c -> (Customer)c) // cast the CommonDTO to a Customer
.map(c -> c.getCustId()) // get the custId
.collect(Collectors.toList()); // capture the custIds in a List
p.s. suggest that you adhere to Java naming conventions (e.g. variable names begin with lower case letter).
I am building a Java Servlets 3.0 REST API and using Gson to serialize some data to json.
I get this error, though:
java.lang.ClassCastException: za.co.a.models.tables.sybase.Family cannot be cast to java.util.Map
at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.write(MapTypeAdapterFactory.java:145)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:69)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:97)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:61)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:69)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:127)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:245)
at com.google.gson.Gson.toJson(Gson.java:704)
at com.google.gson.Gson.toJson(Gson.java:683)
at com.google.gson.Gson.toJson(Gson.java:638)
at com.google.gson.Gson.toJson(Gson.java:618)
at za.co.a.helpers.JsonHelper.toJson(JsonHelper.java:33)
at za.co.a.models.tables.sybase.ActiveProcesses.saveProcess(ActiveProcesses.java:57)
My code is as follows:
#Table(name = "E_FAMILY")
public class Family extends IGenericModel <Family>
{
#Id
public BigDecimal EMPLOYEE_ID;
#Id
public BigDecimal FAMILY_ID;
#Id
public BigDecimal COMPANY_ID;
public String FIRSTNAME;
public String SECONDNAME;
public String SURNAME;
public String RELATION;
public int RELATION_ID;
public String MED_DEPENDENT_YN;
public String TAX_DEPENDENT_YN;
public String GENDER;
public Date BIRTHDATE;
public String TEL_HOME;
public String TEL_WORK;
public String TEL_CELL;
public String E_MAIL;
...
}
The calling code:
public String toJson(Object obj)
{
return gson.toJson(obj);
}
Family, in this case is field in a larger class, however, I'm not having any problems with any other fields or any other classes that are similar. This is the first time, in the year I've been developing this, that this error comes up. Is there a limit to the size or complexity of class Gson can serialize? Or what can I check to see what's causing the error? Or is there a way change this specific mapping, (though I don't understand why Google is trying to map this class to Map)?
Thanks
Sethmo
Edit Including class hierachy
IGenericModel and IGenericReadOnlyModel only contain functions. IModel has 2 members, but I've added it as part of an ExclusionStrategy so that those members don't get serialized.
public class IGenericModel<T> extends IGenericReadOnlyModel
{
}
public class IGenericReadOnlyModel<T> extends IModel
{
}
public class IModel
{
protected String dbEngine;
protected IDatabase db;
}
Edit rest of the code
Ok, the class that holds Family is quite large and mostly full of Strings, Dates and Booleans and ints. Where Family comes in is here, the two objects are passed from the front-end and represent the old and new values (users can edit, add and delete family members in the UI, then submit those lists).
public abstract class IWebProcess extends IModel
{
protected Object _OldValue;
protected Object _NewValue;
}
Once submitted (as JSON from the UI), it's serialized:
Type familyType = new TypeToken<LinkedList<Family>>(){}.getType();
LinkedList<Family> oldFamily = gson.fromJson(oldFamilyJson, familyType);
LinkedList<Family> newFamily = gson.fromJson(newFamilyJson, familyType);
Then, the concrete class is then initialized:
IWebProcess family = WebProcess_FamilyRequisition(oldFamily,newFamily,...,...,...)
then, in the constructor of WebProcess_FamilyRequisition, I call super(oldFamily, newFamily) and then in the constructor of IWebProcess:
this._OldValue = oldFamily
this._NewValue = newFamily
I do all this casting because I save the new values to the DB first, before serializing the entire WebProcess to the DB. I've made _OldValue and _NewValue Objects because this is a base class for 8 other classes that work the same and they serialize just fine.
Let's say I want to build a rest api for storing car information. To make it simple for the sake of this post let's say I would like it to look like this:
/api/cars/{carmake}/save
/api/cars/{carmake}/edit
/api/cars/{carmake}/delete
Now, let's say I have multiple car makes and each of them would require different car make services eg. BmwService, MercedesService, AudiService.
So this is my idea: one abstract controller that would look something like this:
#RequestMapping(value="/api/cars/")
public abstract class CarController {
protected final String CAR_MAKE;
public CarController(String carMake){
this.CAR_MAKE = carMake;
}
#PostMapping(value = CAR_MAKE + "/save")
public abstract void save(#Valid #RequestBody Serializable car)
#DeleteMapping(value = CAR_MAKE + "/delete")
public abstract void save(#Valid #RequestBody Serializable car);
#PatchMapping(value = CAR_MAKE + "/edit")
public abstract void save(#Valid #RequestBody Serializable car)
}
And then an actual controller could look something like this:
#RestController
public class AudiController extends CarController {
private AudiService audiService;
#Autowired
public AudiController(AudiService audiService){
super("audi");
this.audiService = audiService;
}
#Override
public void save(#Valid #RequestBody Serializable car) {
audiService.save((Audi) car);
}
.
.
.
}
The problem is that spring does not allow me to have the value for request mappings with a final variable if it is initialized through the constructor (if the CAR_MAKE is initialized right on the field declaration eg. protected final String CAR_MAKE = "s" it works). So is there any way to work around this so that the paths can come from each subclass?
Not near a compiler but something like this.
Implement a CarService interface:
public interface CarService {
String getBrand();
void save(Car car);
// ...
}
Implement AudiCarService, BmwCarService (etc) types that implement CarService.
Implement a CarService repository something like:
public class CarServiceRepository {
private Map<String, CarService> carServicesByBrand;
public Optional<CarService> findFor(String brand) {
return Optional.ofNullable(carServicesByBrand.get(brand));
}
#Autowired
public void setCarServicesByBrand(List<CarService> carServices) {
this.carServicesByBrand = carServices.stream().collect(Collectors.toMap(CarService::getBrand, Function.identity()));
}
}
Implement a single controller "CarController":
#RequestMapping(value="/api/cars")
#Component
public class CarController {
#Autowired
private CarServiceRepository carServiceRepository;
#PostMapping(value = "/{brand}/save")
public void save(#Valid #RequestBody Serializable car, #PathParam String brand) {
carServiceRepository.findFor(brand).ifPresent(carService -> carService.save(car));
}
// ...
}
Consider also favoring HTTP verbs over explicit verbs in URLs e.g. Why does including an action verb in the URI in a REST implementation violate the protocol?
I have a controller which produces JSON, and from this controller, I return an entity object, which is automatically serialized by Jackson.
Now, I want to avoid returning some fields based on a parameter passed to the controller. I looked at examples where this is done using FilterProperties / Mixins etc. But all the examples I saw requires me to use ObjectMapper to serialize / de-serialize the bean manually. Is there any way to do this without manual serialization? The code I have is similar to this:
#RestController
#RequestMapping(value = "/myapi", produces = MediaType.APPLICATION_JSON_VALUE)
public class MyController {
#Autowired
private MyService myService;
#RequestMapping(value = "/test/{variable}",method=RequestMethod.GET)
public MyEntity getMyEntity(#PathVariable("variable") String variable){
return myservice.getEntity(variable);
}
}
#Service("myservice")
public class MyService {
#Autowired
private MyEntityRepository myEntityRepository;
public MyEntity getEntity(String variable){
return myEntityRepository.findOne(1L);
}
}
#Entity
#Table(name="my_table")
#JsonIgnoreProperties(ignoreUnknown = true)
public class MyEntity implements Serializable {
#Column(name="col_1")
#JsonProperty("col_1")
private String col1;
#Column(name="col_2")
#JsonProperty("col_2")
private String col2;
// getter and setters
}
Now, based on the value of "variable" passed to the controller, I want to show/hide col2 of MyEntity. And I do not want to serialize/deserialize the class manually. Is there any way to do this? Can I externally change the Mapper Jackson uses to serialize the class based on the value of "variable"?
Use JsonView in conjunction with MappingJacksonValue.
Consider following example:
class Person {
public static class Full {
}
public static class OnlyName {
}
#JsonView({OnlyName.class, Full.class})
private String name;
#JsonView(Full.class)
private int age;
// constructor, getters ...
}
and then in Spring MVC controller:
#RequestMapping("/")
MappingJacksonValue person(#RequestParam String view) {
MappingJacksonValue value = new MappingJacksonValue(new Person("John Doe", 44));
value.setSerializationView("onlyName".equals(view) ? Person.OnlyName.class : Person.Full.class);
return value;
}
Use this annotation and set the value to null, it will not be serialised:
#JsonInclude(Include.NON_NULL)
I need to implement custom conversion for ID field in Company and Employee classes. I have already implemented custom converter extended from StrutsTypeConverter and it is successfully used to convert Company.ID field, but it does not work for Employee.ID.
Seems like the main problem is in conversion properties file. How should I specify converter class for employee ID field in conversion properties file?
MyAction-conversion.properties:
company.id = com.struts2.convertors.MyCustomConverter
company.??????.id = com.struts2.convertors.MyCustomConverter
MyAction:
public class MyAction extends ActionSupport {
private Company company;
public Company getCompany () {
return company;
}
public void setCompany (Company company) {
this.company= company;
}
#Override
public String execute() {
return SUCCESS;
}
}
Company:
public class Company {
private ID id;
private List<Employee> employees;
// getters and setters
}
Employee
public class Employee{
private ID id;
private String name;
// getters and setters
}
TypeConversion Annotation:
This annotation is used for class and application wide conversion rules.
The TypeConversion annotation can be applied at property and method level.
#TypeConversion(converter = “com.test.struts2.MyConverter”)
public void setAmount(String amount)
{
this.amount = amount;
}
This annotation specifies the location of one of my converters. literally, by using this annotation, I register my class com.test.struts2.MyConverter as a converter, and gets executed every time when setAmount(String amount) method is invoked.
Try the following by adding a converter for the ID type to the xwork-conversion.properties file
com.struts2.ID = com.struts2.convertors.MyCustomConverter