提供链接而不是序列化集合

时间:2016-02-16 14:54:25

标签: java spring rest jackson jax-rs

我有两个班级:

public class A{
    private String property;

    private Set<B> bs = new HashSet<B>();
}

public class B{
    private String property;

    private A owner;
}

我创建了一个基本的JAX-RS + Spring启动应用程序,我想返回A.

问题是A包含Set<B>,因此我得到了无限的嵌套级别问题。

我找到了一个解决方案:提供链接而不是资源本身,这样我就可以拥有:

{ "property" : "value", "bs" : "http://mywebsite/api/a/2/bs" }

我没有得到任何嵌套级别的问题,因为每个级别都是单独序列化的。

如何在JAX-RS应用程序中实现这样的功能?我没有发现任何关于它的信息,但我知道自Spring Data Neo4j REST使用它以来它是可能的,而且效果很好。

1 个答案:

答案 0 :(得分:0)

我可以想到以下可能的方法来实现这一目标。

使用XmlAdapter

在序列化期间将集转换为URI
@XmlRootElement
public class A
{
    private int id;

    private String property;

    @XmlJavaTypeAdapter(BsAdapter.class)
    private Set<B> bs = new HashSet<B>();
}


public class BsAdapter extends XmlAdapter<URI, Set<B>>
{

    @Override
    public Set<B> unmarshal(URI v) throws Exception
    {
        return new HashSet<>();
    }

    @Override
    public URI marshal(Set<B> v) throws Exception
    {
        return URI.create(
            Optional.ofNullable(v)
            .filter(b -> !b.isEmpty())
            .map(b -> "/" + b.stream().findFirst().get().getOwner().getId() + "/bs")
            .orElse(""));
    }

}

如果A的id是URI的一部分,则只有在该集合不为空时才能检索它。结果是相对URI,因为没有可用的进一步信息。

作为替代方案,手动设置URI。

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class A
{

    private int id;

    private String property;

    @XmlTransient
    private Set<B> bs = new HashSet<B>();

    @XmlElement(name = "bs")
    private URI bsUri;
}

并设置如下网址:

@Context
UriInfo uriInfo;

@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getA()
{
    // ... 
    a.setBsUri(UriBuilder.fromUri(uriInfo.getAbsolutePath())
        .path("a")
        .path("{id}")
        .path("bs")
        .build(2));     
    return Response.ok().entity(a).build();
}

或者,如果HTTP Header中的链接也很好,只需这样做,这样就不必使用URI扩展A类。

    return Response.ok().entity(a).link(UriBuilder.fromUri(uriInfo.getAbsolutePath())
        .path("a")
        .path("{id}")
        .path("bs")
        .build(2), "bs").build();