对于Jersey/JAXB,是否可以将同一个POJO同时用作“父级”和“子级”,但在用作“子级”时删除某些属性?

3
我有两个Java对象,Company和Employee。它们都带有Jersey/JAXB注解,这样我就可以通过HTTP GET调用RESTful服务来获取它们。实际上,Company是Employee的"父"对象,我想在JSON响应中为子Employee列表提供一个属性的子集,如果获取Company,则只返回该子集。但是,当获取Employee本身时,我仍然希望返回所有Employee的属性。
我一直试图理解这一点,但是我不太确定如何做到这一点。这似乎很基础,但我没有在网上找到任何示例(也许我搜索了错误的内容)。
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...
}

现在,我有一个RESTful服务来获取Company对象和Employee对象的HTTP GET请求:
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) {
  //....
  }
}

为了获取员工对象,所有属性都将被返回,正如预期的那样。因此它看起来像这样:
{
  "employeeId": "1A2-B35",
  "employeeName": "John Doe",
  "employeeType": "Engineer"
}

当对公司对象执行GET时,我希望在公司对象下的员工列表中仅返回一些指定的员工属性(例如:仅返回员工姓名和员工ID)。因此,我期望的JSON响应应该类似于以下内容:

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

然而,它当然会以这样的形式返回 employees 列表中所有员工属性:
{
  "companyName": "Acme",
  "companyType": "Services",
  "employees": [
    {
      "employeeId": "123_ABC",
      "employeeName": "John Doe",
      "employeeType": "Engineer"
    },
    {
      "employeeId": "456_XYZ",
      "employeeName": "Jane Doe",
      "employeeType": "Executive"
    }
  ]
}

只使用注释就能实现这个吗?还是除了创建自己的JSON对象之外,还有其他方法可以做到这一点?

2个回答

4
你可以使用XmlAdapter来解决这个问题:

EmployeeAdapter

XmlAdapter会将完全填充的Employee转换为另一个实例,只填充要驻留到XML的字段。由于这是副本,因此不会影响原始对象模型。

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;
    }

}

公司

@XmlJavaTypeAdapter注解用于指定所需属性上的XmlAdapter

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...
}

更多信息


我注意到你在代码中有以下注释:

// getters and setters with @XmlElement on each attribute

因为JAXB (JSR-222)实现是通过异常配置的,所以您只需要在需要从默认XML表示中添加注释的位置添加注释。这意味着您可以删除模型中的某些注释:


Blaise,感谢您的回答! 它几乎是完美的。 但是,我在布尔属性方面遇到了一些奇怪的问题。 我为Employee类有一个布尔属性没有编组到Employee的 XmlAdapter类中。 然而,在调用HTTP GET Company时,Employee对象的布尔属性与我的其他编组属性一起出现。 所有我没有编组的其他属性都不会出现在响应中,这是预期的。 您知道为什么布尔属性可能会出现吗? - richsinn
啊,关于我的@XmlElement的使用,是因为我实际上修改了每个元素的显示名称:即@XmlElement(name=EMPLOYEE_NAME_FIELD)(只是上面的伪代码中没有写下来)。我想知道这是否会对我遇到的布尔问题产生影响。 - richsinn
1
@theSshow - boolean属性显示出来是因为它始终具有truefalse的值。我的答案利用了JAXB默认行为来编组null值(请参见http://blog.bdoughan.com/2012/04/binding-to-json-xml-handling-null.html)。您仍然可以利用这种方法,但是需要使用`XmlAdapter`将`Employee`转换为`AdaptedEmployee`而不是`Employee`。 - bdoughan
Blaise,再次感谢您的回复。我不完全清楚您的意思:“XmlAdapterEmployee转换为AdaptedEmployee而不是Employee。”您建议的AdaptedEmployee是否另一个扩展Employee的类,然后在扩展的类上使用XmlAdapter?(另外,我已默认使所有属性不可为空,因此对我来说,空值的编组不是问题)。 - richsinn
1
@theSshow - 我建议如果您无法将boolean属性更改为Boolean(以支持空值),那么您可以创建一个AdaptedEmployee类(它不应扩展Employee),以便与XmlAdapter一起使用(请参见http://blog.bdoughan.com/2010/12/jaxb-and-immutable-objects.html)。 - bdoughan
谢谢您的建议,将其更改为布尔值确实起作用了。但是,我现在该如何确保JSON响应返回JavaScript布尔类型的值,而不是字符串(即true vs. "true")? - richsinn

1

我曾经在接口方面做过类似的事情。创建一个IEmployee和IEmployeeLight接口。

IEmployeeLight包含基本信息,IEmployee扩展它并添加完整的数据。您可以使集合返回IEmployeeLight类型。适当地注释两个接口。您可能需要在服务定义上使用XmlSeeAlso,因为JAXB无法绑定到接口,除非它有一个实现提示。

如果这不够详细,很抱歉,这是一个复杂的话题,没有白板很难做到公正。

请注意,我并不是说这个解决方案是最优的,只是我们使用过的。

我们的用例更加复杂,涉及DTO、JPA实体、版本控制等。


Jeff,谢谢您的回复。我会尝试您的建议并与您联系。 - richsinn
我认为这种方法也可以行得通,但我决定继续使用XmlAdapter方法。 - richsinn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接