1

I have two classes:

public class A{
    private String property;

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

public class B{
    private String property;

    private A owner;
}

I created a basic JAX-RS + Spring boot application, and I want to return A.

Problem is that A contains a Set<B>, so I get an infinite nested level problem.

I found a solution: provide a link instead of the resource itself, this way I can have this:

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

And i don't get any nested level problem, since each level is serialized seperately.

How can I implement such a thing in my JAX-RS application? I found nothing about it but I know it's possible since Spring Data Neo4j REST is using it, and it works well.

Supamiu
  • 8,501
  • 7
  • 42
  • 76
  • Stop using jax-rs -- it's not capable of doing this without a lot of work. Start using spring data rest instead. There are no standards for this type of thing, but spring has chosen a particular implementation that's currently useful. – Software Engineer Feb 16 '16 at 16:08
  • @EngineerDollery Problem is that Spring Data Rest gives me this problem: http://stackoverflow.com/questions/35412463/how-to-add-resource-and-specify-related-element – Supamiu Feb 16 '16 at 17:08
  • Check [this out](http://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion) – Paul Samsotha Feb 17 '16 at 01:24
  • @peeskillet your example is good, but it doesn't allow me to provide links instead of item collection. I think I'm looking for some hal+json. – Supamiu Feb 17 '16 at 08:23

1 Answers1

0

I can think of the following possible ways how to achieve that.

Convert the set to URI during serialization by using a XmlAdapter

@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(""));
    }

}

If the id of A is part of the URI then it can only be retrieved if the set is not empty. The result is a relative URI, since there are no further information available.

As an alternative set the URI manually.

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

And set the URL like this:

@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();
}

Or if a link in HTTP Header is also fine, simply do it this way so you do not have to extend class A with the URI.

    return Response.ok().entity(a).link(UriBuilder.fromUri(uriInfo.getAbsolutePath())
        .path("a")
        .path("{id}")
        .path("bs")
        .build(2), "bs").build();
simdevmon
  • 673
  • 7
  • 14
  • Your idea is good, but I solved the problem using Spring-HATEOAS to provide hal+json https://spring.io/guides/gs/rest-hateoas/ – Supamiu Feb 19 '16 at 09:46