For Jersey/JAXB, is it possible to use the same POJO as both 'parent' and 'child' but remove certain attributes when being used as a 'child'?

Question!

I have two Java objects, Company and Employee. They are both annotated with Jersey/JAXB annotations so that I can call RESTful services to HTTP GET them. Essentially, Company is the "parent" object to Employee, and what I want to do is provide only a subset of the attributes for the child list of Employees in the JSON response if doing a GET for the Company. But, I still want to return all the attributes for the Employee when doing a GET for the Employee itself.

I've been trying to get my head around this, but I'm not quite sure how to do it. It seems pretty basic, but I haven't been able to find any examples online (maybe I'm google searching the wrong things).

Company.java

@XmlRootElement
public class Company {
  private String companyName;
  private String companyType;
  private List<Employee> employees = new ArrayList<Employee>();

  // getters and setters with @XmlElement on each attribute 
  //...etc...
  @XmlElement
  public List<Employee> getEmployees() {
    return employees;
  }
  //...etc...
}

Employee.java

@XmlRootElement
public class Employee{
  private String employeeId;
  private String employeeName;
  private String employeeType;

  // getters and setters with @XmlElement on each attribute
  //...etc...
  @XmlElement
  public String getEmployeeId() {
    return employeeId;
  }
  //...etc...
}

Now, I have a RESTful service to do an HTTP GET on the Company object and the Employee object:

public class infoService{
  @GET
  @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
  public Response getCompany(String Id) {
  //....
  }

  @GET
  @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
  public Response getEmployee(String Id) {
  //....
  }
}

For getting the Employee object, all the attributes are returned, as expected. So it looks something like this:

{
  "employeeId": "1A2-B35",
  "employeeName": "John Doe",
  "employeeType": "Engineer"
}

When doing a GET for the Company object, I want the list of Employees under the Company object to return only a couple specified attributes for the Employee (i.e. employee name and employee id only). Therefore, my desired JSON response would look something like this:

{
  "companyName": "Acme",
  "companyType": "Services",
  "employees": [
    {
      "employeeId": "123_ABC",
      "employeeName": "John Doe"
    },
    {
      "employeeId": "456_XYZ",
      "employeeName": "Jane Doe"
    }
  ]
}

However, of course, it returns all the Employee attributes in the employees list, like this:

{
  "companyName": "Acme",
  "companyType": "Services",
  "employees": [
    {
      "employeeId": "123_ABC",
      "employeeName": "John Doe",
      "employeeType": "Engineer"
    },
    {
      "employeeId": "456_XYZ",
      "employeeName": "Jane Doe",
      "employeeType": "Executive"
    }
  ]
}

Is this possible to do this solely with the annotations? Or is there some other way of doing this short of creating my own JSON object?

By : richsinn


Answers
You could use an XmlAdapter to solve this use case:

EmployeeAdapter

The XmlAdapter will convert the fully populated Employee to another instance with only the fields you want marshalled to XML populated. Since this is a copy it does not affect the original object model.

package forum11059499;

import javax.xml.bind.annotation.adapters.XmlAdapter;

public class EmployeeAdapter extends XmlAdapter<Employee, Employee>{

    @Override
    public Employee marshal(Employee employee) throws Exception {
        Employee partialEmployee = new Employee();
        partialEmployee.setEmployeeId(employee.getEmployeeId());
        partialEmployee.setEmployeeName(employee.getEmployeeName());
        return partialEmployee;
    }

    @Override
    public Employee unmarshal(Employee employee) throws Exception {
        return employee;
    }

}

Company

The @XmlJavaTypeAdapter annotation is used to specify the XmlAdapter on the desired property.

package forum11059499;

import java.util.*;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

@XmlRootElement
public class Company {
    private String companyName;
    private String companyType;
    private List<Employee> employees = new ArrayList<Employee>();

    // getters and setters with @XmlElement on each attribute
    // ...etc...
    @XmlElement
    @XmlJavaTypeAdapter(EmployeeAdapter.class)
    public List<Employee> getEmployees() {
        return employees;
    }
    // ...etc...
}

For More Information


I noticed that you had the following comment in your code:

// getters and setters with @XmlElement on each attribute

Because JAXB (JSR-222) implementations are configuration by exception, you only need to add annotations where you want the XML representation from the default. This means you may be able to remove some of the annotations you have in your model:



I have done just this with interfaces. Make an IEmployee and IEmployeeLight interface.

IEmployeeLight has the basics, IEmployee extends it and adds the full fat data. You can make the collections return a type of IEmployeeLight. Annotate both interfaces up appropriately. You'll probably need to use XmlSeeAlso on your service definitions as JAXB can't bind to interfaces unless it has an implementation hint.

Sorry if that's not enough detail, it's a complex topic and hard to do it justice without a whiteboard.

Note that I'm not saying this solution is optimal, just what has worked for us.

Our use case was more complex with DTOs, JPA entities, versioning etc.



This video can help you solving your question :)
By: admin