Como reutilizar Jersey JSON / JAXB para serialização?
Pergunta
Eu tenho um serviço JAX-RS RESTO implementado usando Jersey. Um dos recursos interessantes do JAX-RS / Jersey é a facilidade com que um POJO pode ser transformado em um serviço REST, simplesmente por aspersão algumas anotações Java ... incluindo um mecanismo trivialmente fácil para traduzir POJOs para JSON -. Usando anotações JAXB
Agora, eu gostaria de ser capaz de tirar partido desta funcionalidade legal para fins não-Rest JSON-ifying - Eu adoraria ser capaz de simplesmente serializar alguns desses objetos para o disco, como texto JSON. Aqui está um objeto exemplo JAXB que eu gostaria de serialize:
@XmlRootElement(name = "user")
public class UserInfoImpl implements UserInfo {
public UserInfoImpl() {}
public UserInfoImpl(String user, String details) {
this.user = user;
this.details = details;
}
public String getUser() { return user; }
public void setUser(String user) { this.user = user; }
public String getDetails() { return details; }
public void setDetails(String details) { this.details = details; }
private String user;
private String details;
}
Jersey pode transformar um deles em JSON com nenhuma informação adicional. Eu estou querendo saber se Jersey expôs essa funcionalidade na API para as necessidades como a minha? Eu tive nenhuma sorte é tão longe ...
Obrigado!
Atualização 2009-07-09 : Eu aprendi que eu posso usar os provedores opor a quase fazer exatamente o que eu quero:
@Context Providers ps;
MessageBodyWriter uw = ps.getMessageBodyWriter(UserInfoImpl.class, UserInfoImpl.class, new Annotation[0], MediaType.APPLICATION_JSON_TYPE);
uw.writeTo(....)
... Este escreve o objeto como JSON para qualquer outputstream, o que seria perfeito para mim, mas eu só posso chegar aos Provedores de objeto usando @Context de um objeto @Component. Alguém sabe como acessá-lo a partir de um POJO regular, un-anotada? Obrigado!
Solução
Jersey usa um par de estruturas diferentes dependendo se você usar mapeada (), badgerfish (), ou notação naturais (). Natural é geralmente aquele que as pessoas querem. E que é implementado utilizando o muito bom (e muito rápido) standalone processador Jackson JSON, creio eu, que vai de Objeto-> JAXB-> JSON. No entanto Jackson também fornece seu próprio provedor JAX-RS para ir Objeto direto> JSON.
Na verdade, eles ainda acrescentou suporte para anotações JAXB. Ter um olhar para
http://wiki.fasterxml.com/JacksonJAXBAnnotations
Eu acho que é em última análise, o que você está procurando. Jackson faz objeto <-> Processamento de JSON ... Jersey só faz as chamadas para você
Outras dicas
Aqui está um exemplo simples breve do uso de JAXB para mapear objetos para JSON (usando Jackson):
http://ondra.zizka.cz/ stranky / programovani / java / jaxb-json-jackson-howto.texy
ObjectMapper mapper = new ObjectMapper();
String str = mapper.writeValueAsString(pojoObject);
JAXB anotações funcionam bem quando a serialização de XML. O principal problema é que JAXB não suporta matrizes vazias. Então, quando a serialização de algo assim ...
List myArray = new ArrayList();
... para json via jaxb anottations todas as suas matrizes vazias tornam-se nulos em vez de [].
Para resolver isso, você pode simplesmente serializar seus pojos diretamente para json via jackson.
Dê uma olhada neste partir guia do usuário de Jersey: http://jersey.java.net/nonav/documentation/latest /user-guide.html#d0e1959
Esta é a melhor maneira de usar o provedor Jackson sem JAXB. Além disso, você sempre pode usar a versão mais recente de jackson por downlaoding jackson-all-x.y.z-jar de sua web.
Este método não irá interferir com suas anotações JAXB então eu sugiro para ter uma tentativa!
Desde Jersey é uma implementação de referência de JAX-RS e JAX-RS é focada completamente em fornecer uma maneira padrão de implementação do ponto final para o serviço REST as questões de serialização a carga é deixado para outras normas.
Eu acho que se eles incluíram serialização objeto nas JAX-RS padrão seria rapidamente tornar-se uma besta multi-headed grande que seria difícil de implementar e perder algum dele é foco.
I apreciar como focada Jersey é no fornecimento limpo e simples de endpoints uso REST. No meu caso eu tenho apenas uma subclasse um pai que tem todas as canalizações JAXB nele para mobilizar objetos entre binário e XML é muito limpo.
Com um pouco de bootstrapping específica Jersey, você pode usá-lo para criar os objetos JSON necessárias para você. Você precisa incluir as seguintes dependências (você pode usar pacote, mas vai causar problemas se você estiver usando Weld para testes):
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-json</artifactId>
<version>1.12</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
<version>1.12</version>
</dependency>
De lá você pode criar uma classe JAXB anotado. O seguinte é um exemplo:
@XmlRootElement
public class TextMessage {
private String text;
public String getText() { return text; }
public void setText(String s) { this.text = text; }
}
Em seguida, você pode criar o seguinte teste de unidade:
TextMessage textMessage = new TextMessage();
textMessage.setText("hello");
textMessage.setUuid(UUID.randomUUID());
// Jersey specific start
final Providers ps = new Client().getProviders();
// Jersey specific end
final MultivaluedMap<String, Object> responseHeaders = new MultivaluedMap<String, Object>() {
@Override
public void add(final String key, final Object value) {
}
@Override
public void clear() {
}
@Override
public boolean containsKey(final Object key) {
return false;
}
@Override
public boolean containsValue(final Object value) {
return false;
}
@Override
public Set<java.util.Map.Entry<String, List<Object>>> entrySet() {
return null;
}
@Override
public List<Object> get(final Object key) {
return null;
}
@Override
public Object getFirst(final String key) {
return null;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public Set<String> keySet() {
return null;
}
@Override
public List<Object> put(final String key, final List<Object> value) {
return null;
}
@Override
public void putAll(
final Map<? extends String, ? extends List<Object>> m) {
}
@Override
public void putSingle(final String key, final Object value) {
}
@Override
public List<Object> remove(final Object key) {
return null;
}
@Override
public int size() {
return 0;
}
@Override
public Collection<List<Object>> values() {
return null;
}
};
final MessageBodyWriter<TextMessage> messageBodyWriter = ps
.getMessageBodyWriter(TextMessage.class, TextMessage.class,
new Annotation[0], MediaType.APPLICATION_JSON_TYPE);
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
Assert.assertNotNull(messageBodyWriter);
messageBodyWriter.writeTo(textMessage, TextMessage.class,
TextMessage.class, new Annotation[0],
MediaType.APPLICATION_JSON_TYPE, responseHeaders, baos);
final String jsonString = new String(baos.toByteArray());
Assert.assertTrue(jsonString.contains("\"text\":\"hello\""));
A vantagem dessa abordagem é que mantém tudo dentro da API JEE6, há bibliotecas externas são explicitamente necessário, exceto para testar e recebendo os fornecedores. No entanto, você precisa criar uma implementação de MultivaluedMap porque não há nada previsto na norma e nós realmente não usá-lo. É pode também ser mais lento do que Gson, e muito mais complicado do que o necessário.
Eu entendo visões XML mas teria mostrado alguma previsão para exigir apoio JSON para POJOs como equipamento standard. Tendo em médico até identificadores JSON com caracteres especiais não faz sentido se a sua implementação é JSON e seu cliente é uma RIA JavaScript.
Além disso, não que Java Beans não são POJOs. Eu gostaria de usar algo como isto na superfície externa do meu web níveis:
public class Model
{
@Property height;
@Property weight;
@Property age;
}
No construtor padrão, nenhum ruído getter / setter, apenas um POJO com as minhas próprias anotações.