Pergunta

Estou usando o Jersey para criar um serviço Web REST para um componente do servidor.

O objeto anotado por Jaxb que eu quero serializar em uma lista é assim:

@XmlRootElement(name = "distribution")
@XmlType(name = "tDistribution", propOrder = {
    "id", "name"
})
public class XMLDistribution {
    private String id;
    private String name;
    // no-args constructor, getters, setters, etc
}

Eu tenho um recurso de descanso para recuperar uma distribuição que se parece com o seguinte:

@Path("/distribution/{id: [1-9][0-9]*}")
public class RESTDistribution {
    @GET
    @Produces("application/json")
    public XMLDistribution retrieve(@PathParam("id") String id) {
        return retrieveDistribution(Long.parseLong(id));
    }
    // business logic (retrieveDistribution(long))
}

Eu também tenho um recurso de descanso para recuperar uma lista de todas as distribuições, que se parecem com a seguinte:

@Path("/distributions")
public class RESTDistributions {
    @GET
    @Produces("application/json")
    public List<XMLDistribution> retrieveAll() {
        return retrieveDistributions();
    }
    // business logic (retrieveDistributions())
}

Eu uso um contextresolver para personalizar a serialização do JAXB, que está atualmente configurada como esta:

@Provider
@Produces("application/json")
public class JAXBJSONContextResolver implements ContextResolver<JAXBContext> {
    private JAXBContext context;
    public JAXBJSONContextResolver() throws Exception {
        JSONConfiguration.MappedBuilder b = JSONConfiguration.mapped();
        b.nonStrings("id");
        b.rootUnwrapping(true);
        b.arrays("distribution");
        context = new JSONJAXBContext(b.build(), XMLDistribution.class);
    }
    @Override
    public JAXBContext getContext(Class<?> objectType) {
        return context;
    }
}

Ambos os recursos de repouso funcionam, bem como o resolvedor de contexto. Este é um exemplo de saída para o primeiro:

// path: /distribution/1
{"id":1,"name":"Example Distribution"}

Que é exatamente o que eu quero. Este é um exemplo de saída para a lista:

// path: /distributions
{"distribution":[{"id":1,"name":"Sample Distribution 1"},{"id":2,"name":"Sample Distribution 2"}]}

O que não é exatamente o que eu quero.

Eu não entendo por que há um anexo distribution etiqueta lá. Eu queria removê -lo com .rootUnwrapping(true) no resolvedor de contexto, mas aparentemente isso remove apenas outra tag anexando. Esta é a saída com .rootUnwrapping(false):

// path: /distribution/1
{"distribution":{"id":1,"name":"Example Distribution"}} // not ok
// path: /distributions
{"xMLDistributions":{"distribution":[{"id":1,"name":"Sample Distribution 1"},{"id":2,"name":"Sample Distribution 2"}]}}

Eu também tive que configurar .arrays("distribution") Para sempre obter uma matriz JSON, mesmo com apenas um elemento.

Idealmente, eu gostaria de ter isso como uma saída:

// path: /distribution/1
{"id":1,"name":"Example Distribution"} // currently works
// path: /distributions
[{"id":1,"name":"Sample Distribution 1"},{"id":2,"name":"Sample Distribution 2"}]

Eu tentei devolver um List<XMLDistribution>, uma XMLDistributionList (invólucro em torno de uma lista), um XMLDistribution[], mas não consegui encontrar uma maneira de obter uma simples variedade de distribuições JSON no meu formato necessário.

Eu também tentei as outras anotações devolvidas por JSONConfiguration.natural(), JSONConfiguration.mappedJettison(), etc, e não conseguia nada parecido com o que eu preciso.

Alguém sabe se é possível configurar o JAXB para fazer isso?

Foi útil?

Solução

Encontrei uma solução: substitua o serializador Jaxb JSON por um serializador JSON melhor comportado como Jackson. A maneira mais fácil é usar Jackson-Jaxrs, que já fez isso para você. A classe é JacksonjsonProvider. Tudo o que você precisa fazer é editar o web.xml do seu projeto para que Jersey (ou outra implementação Jax-RS) digitalize. Aqui está o que você precisa adicionar:

<init-param>
  <param-name>com.sun.jersey.config.property.packages</param-name>
  <param-value>your.project.packages;org.codehaus.jackson.jaxrs</param-value>
</init-param>

E isso é tudo o que há para isso. Jackson será usado para a serialização do JSON e funciona da maneira que você espera para listas e matrizes.

A maneira mais longa é escrever seu próprio MessageBodyWriter personalizado registrado para produzir "Application/JSON". Aqui está um exemplo:

@Provider
@Produces("application/json")
public class JsonMessageBodyWriter implements MessageBodyWriter {
    @Override
    public long getSize(Object obj, Class type, Type genericType,
            Annotation[] annotations, MediaType mediaType) {
        return -1;
    }

    @Override
    public boolean isWriteable(Class type, Type genericType,
            Annotation annotations[], MediaType mediaType) {
        return true;
    }

    @Override
    public void writeTo(Object target, Class type, Type genericType,
            Annotation[] annotations, MediaType mediaType,
            MultivaluedMap httpHeaders, OutputStream outputStream)
            throws IOException {        
        new ObjectMapper().writeValue(outputStream, target);
    }
}

Você precisará garantir que seu web.xml inclua o pacote, como para a solução pronta acima.

De qualquer maneira: pronto! Você verá JSON corretamente formado.

Você pode baixar Jackson aqui:http://jackson.codehaus.org/

Outras dicas

A resposta de Jonhatan é ótima e tem sido muito útil para mim.

Apenas uma atualização:

Se você usar a versão 2.x de Jackson (por exemplo, versão 2.1), a classe é com.fasterxml.jackson.jaxrs.json.jacksonjaxbjsonprovider, portanto o web.xml é:

<init-param>
  <param-name>com.sun.jersey.config.property.packages</param-name>
  <param-value>your.project.packages;com.fasterxml.jackson.jaxrs.json</param-value>
</init-param>
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top