[RESOLVIDO] Lista com JSON + Vraptor + Restfulie

18 respostas
A

Tenho o controller com o seguinte método:

@Get
    @Path("/categoria")
    public void getCategorias() {
        List<Categorias> categorias = dao.findAll();

        if (categorias != null && !categorias.isEmpty()) {
            result.use(Results.representation()).from(categorias).recursive().serialize();
        } else {
            result.use(Results.status()).noContent();
        }

    }

Tenho o seguinte customjsonserialization:

@Component
public class CustomJSONSerialization extends XStreamJSONSerialization {

    public CustomJSONSerialization(HttpServletResponse response, TypeNameExtractor extractor,
            ProxyInitializer initializer, XStreamBuilder builder) {
        super(response, extractor, initializer, builder);
    }

    @Override
    @SuppressWarnings("deprecation")
    protected XStream getXStream() {

        XStream xstream = super.getXStream();
        xstream.registerConverter(new CollectionConverter(xstream.getMapper()) {
            @Override
            @SuppressWarnings("rawtypes")
            public boolean canConvert(Class type) {
                return Collection.class.isAssignableFrom(type);
            }
        });

        return xstream;
    }
}

e o seguinte teste:

@Test
    public void shouldBeAbleToGetAnListOfCategorias2() throws IOException {
        String response = restfulie.at("http://localhost:8084/site/categoria")
                .accept("application/json").get().getContent();
        List<Categorias> categorias = new Gson().fromJson(response, new TypeToken<List<Categorias>>(){}.getType());
        System.out.println(response);
        Assert.assertNotNull(categorias);  
    }

minha classe Categorias:

@XStreamAlias("categoria")
public class Categorias implements Serializable {


    private Long idcategoria;

    private String descricao;
    
//gets e sets
    
}

O PROBLEMA:
o json gerado (que é escrito pelo system.out acima) fica assim:

e isso causa o seguinte erro:
IllegalStateException: Excepted BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2
com.google.gson.JsonSyntaxException

Alguém sabe como contornar este problema?

18 Respostas

L

pelo erro: Excepted BEGIN_ARRAY but was BEGIN_OBJECT

acho que ele espera:

[{"idcategoria": 1,"descricao": "categoria 1"},{"idcategoria": 2,"descricao": "teste"},{"idcategoria": 3,"descricao": "teste"}]

pra aceitar com {“list”: …} vc precisa criar uma classe qqer:

public class Lista {
   private List<Categorias> list;
}
A

Isso mesmo ele espera como você falou mas como que eu gero sem o “list” lucas? kk

L

com o representation() não dá =(

só se vc fizer direto o JSON:

result.use(Results.json()).withoutRoot().from(lista).recursive().serialize();
A

Blz Lucas eu tive que fazer 2 coisas

1 - para funcionar o json:

if(request.getHeader("Accept").contains("json")){
                    result.use(Results.json()).withoutRoot().from(categorias).recursive().serialize();
            }else{
                result.use(Results.xml()).from(categorias).recursive().serialize();
            }

2 - Não estava chamando os custom serialization tanto xml quanto json, aí fiz a seguinte classe:

@Component  
@PrototypeScoped  
public class CustomXStreamBuilder extends XStreamBuilderImpl {  
  
    public CustomXStreamBuilder(XStreamConverters converters, TypeNameExtractor extractor) {  
        super(converters, extractor);  
    }  
  
    @Override  
    public XStream configure(XStream xstream) {  
        XStream instance = super.configure(xstream);  
        instance.processAnnotations(Categorias.class);  
  
        VRaptorClassMapper mapper = ((VRaptorXStream) instance).getVRaptorMapper();  
        mapper.setSerializee(new Serializee());  
        return instance;  
    }
    
}

É isso mesmo? Preciso de 3 classes pra serializar xml e json?

L

Na verdade, no seu caso vc não precisa da CustomJSONSerialization…

crie um converter que estende de collection converter, anotando ele com @Component.

Já é o suficiente.

http://www.guj.com.br/java/299755-vraptor-serilization-json-liststring#1592956

A

Baah Lucas eu acho que to viajando em alguma coisa aí eu retirei tudo (CustomJSONSerialization, CustomXMLSerialization, BetterCollectionConverter deixei apenas o CustomXStreamBuilder como acima, e resultado ficou assim:

no teste de consulta que retorna um xml do vraptor (server) para o restfulie (client) funcionou.

no teste de consulta que retorna um json do vraptor (server) para o restfulie (client) funcionou.

no teste de que faz um POST xml do restfulie (client) para o vraptor (server) funcionou.

no teste de que faz um POST json do restfulie (client) para o vraptor (server) não funcionou.

Creio que esse POST com json não seja possível, é isso?

este ultimo teste é esse:

@Test
    public void shouldBeAbleToPostAnItemWithHypermedia2() throws Exception {
        Categorias categoria = new Categorias();
        categoria.setDescricao("teste");        
        restfulie = Restfulie.custom();
        JsonMediaType type = new JsonMediaType();
        restfulie.getMediaTypes().register(type);
        Response response = restfulie
                .at("http://localhost:8084/site/categoria")
                .accept("application/json")
                .as("application/json").post(categoria);
        Categorias savedItem = response.getResource();
        Assert.assertNotNull(response);
        
    }

OBS.: o erro está dando no Servidor (Vraptor), Talvez eu possa criar um CustomJSONDeserialization?:

com.thoughtworks.xstream.mapper.CannotResolveClassException: br.com.rsyssoftwares.Categorias
	at com.thoughtworks.xstream.mapper.DefaultMapper.realClass(DefaultMapper.java:56)
	at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:30)
	at com.thoughtworks.xstream.mapper.DynamicProxyMapper.realClass(DynamicProxyMapper.java:55)
at com.thoughtworks.xstream.mapper.PackageAliasingMapper.realClass(PackageAliasingMapper.java:88)
	at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:30)
	at com.thoughtworks.xstream.mapper.ClassAliasingMapper.realClass(ClassAliasingMapper.java:79)
	at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:30)
	at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:30)
	at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:30)
	at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:30)
	at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:30)
	at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:30)
	at com.thoughtworks.xstream.mapper.ArrayMapper.realClass(ArrayMapper.java:74)
	at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:30)
	at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:30)
	at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:30)
	at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:30)
	at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:30)
	at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:30)
	at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:30)
	at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:30)
	at com.thoughtworks.xstream.mapper.CachingMapper.realClass(CachingMapper.java:45)
	at com.thoughtworks.xstream.core.util.HierarchicalStreams.readClassType(HierarchicalStreams.java:29)
	at com.thoughtworks.xstream.core.TreeUnmarshaller.start(TreeUnmarshaller.java:133)
	at com.thoughtworks.xstream.core.AbstractTreeMarshallingStrategy.unmarshal(AbstractTreeMarshallingStrategy.java:32)
	at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:1058)
	at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:1042)
	at com.thoughtworks.xstream.XStream.fromXML(XStream.java:922)
	at br.com.caelum.vraptor.deserialization.JsonDeserializer.deserialize(JsonDeserializer.java:62)
L

br.com.rsyssoftwares.Categorias

existe essa classe na aplicação servidor?

A

Sim no servidor e no client anotadas com @XStreamAlias(“categoria”)

L

Se eu não me engano, vc precisa fazer algo assim:

JsonMediaType type = new JsonMediaType().withTypes(Categorias.class);

pra ele processar as anotações do XStream.

A

Consegui fazer do jeito que eu queria, abaixo seguem os passos que fiz, se acharem que tá errado por favor me informem pra mim corrigir:
Valew lucas e tá aí se alguem precisar:

1: adicionar o seguinte parametro no web.xml

br.com.caelum.vraptor.packages br.com.caelum.vraptor.restfulie,br.com.caelum.vraptor.deserialization.gson

2: Criar a seguinte classe:

@Component  
@PrototypeScoped  
public class CustomXStreamBuilder extends XStreamBuilderImpl {  
  
    public CustomXStreamBuilder(XStreamConverters converters, TypeNameExtractor extractor) {  
        super(converters, extractor);  
    } 
    @Override  
    public XStream configure(XStream xstream) {  
        XStream instance = super.configure(xstream);  
        instance.processAnnotations(Categorias.class); 
        VRaptorClassMapper mapper = ((VRaptorXStream) instance).getVRaptorMapper();  
        mapper.setSerializee(new Serializee());  
        return instance;  
    }
}

3- Mais etas 2 Classes para o vraptor deserializar por elas:

@Component  
public class GsonDeserialize extends GsonDeserialization {  

    public GsonDeserialize(ParameterNameProvider paramNameProvider, List<JsonDeserializer> adapters) {
        super(paramNameProvider, adapters);
    }
  
    
}
@Component  
@ApplicationScoped  
public class Deserializers extends DefaultDeserializers {  
   @Override  
   public Deserializer deserializerFor(String contentType, Container container) {  
      if ("application/json".equals(contentType)) {  
          return container.instanceFor(GsonDeserialize.class);  
      }  
      return super.deserializerFor(contentType, container);  
   }  
}

4- E criar a classe customizada que seta os atributos do json para o objeto:

@Component
public class MyJSONDeserializer implements JsonDeserializer<Categorias> {

    
  @Override
  public Categorias deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) {
    Categorias categoria = new Categorias();
    try{
          JsonObject obj = json.getAsJsonObject();
          if(!obj.get("idcategoria").isJsonNull()){
            categoria.setIdcategoria(obj.get("idcategoria").getAsLong());
          }
          categoria.setDescricao(obj.get("descricao").getAsString());
      }catch(JsonParseException|IllegalStateException ex){
      }

    return categoria;
  }
}

5- Para testar:

public class RestCategoriasTest {
    
    private RestClient restfulie;
    
    @Before
    public void setUp() throws Exception {
        restfulie = Restfulie.custom();
        restfulie.getMediaTypes().register(
                new XmlMediaType().withTypes(Categorias.class));
        
    }

    /*
     * Testing get a simple resource list of server.
     */
    @Test
    public void testarGetListXML() throws IOException {
        Response response = restfulie.at("http://localhost:8084/site/categoria")
                .accept("application/xml").as("application/xml").get();
        List<Categorias> obj = response.getResource();
        Assert.assertNotNull(obj);
        
    }

    @Test
    public void testarGetListJSON() throws IOException {
        String response = restfulie.at("http://localhost:8084/site/categoria")
                .accept("application/json").as("application/json").get().getContent();
        List<Categorias> obj = new Gson().fromJson(response, new TypeToken<List<Categorias>>(){}.getType());
        Assert.assertNotNull(obj);
        
    }
    /*
     *  Testing saving some new object into server.
     */
    @Test
    public void testarPostXML() throws Exception {
        Categorias categoria = new Categorias();
        categoria.setDescricao("teste");
        Response response = restfulie
                .at("http://localhost:8084/site/categoria")
                .accept("application/xml")
                .as("application/xml").post(categoria);
        
        Categorias savedItem = response.getResource();
        Assert.assertNotNull(savedItem);
        
    }
    @Test
    public void testarPostJSON() throws Exception {
        Categorias categoria = new Categorias();
        categoria.setDescricao("teste");
        Gson gson = new Gson();
        JsonElement je = gson.getAdapter(Categorias.class).nullSafe().toJsonTree(categoria);
        JsonObject jo = new JsonObject();
        jo.add("categoria", je);
        restfulie.getMediaTypes().register(new JsonMediaType());
        String response = restfulie
                .at("http://localhost:8084/site/categoria")
                .accept("application/json")
                .as("application/json").post(jo.toString()).getContent();
        Categorias savedItem = gson.fromJson(response, Categorias.class);
        Assert.assertNotNull(savedItem);
        
    }
}
G

Lucas Cavalcanti:
Se eu não me engano, vc precisa fazer algo assim:

JsonMediaType type = new JsonMediaType().withTypes(Categorias.class);

pra ele processar as anotações do XStream.

Boa tarde,
vou precisar de desenterrar este topico :slight_smile:

Este código seria perfeito se funcionasse, mas este metodo nao existe no JsonMediaType, apenas no XML.

Ha alguma forma de configurar de uma forma facil como a apresentada? para o xml faço:
restfulie.getMediaTypes().register(new XmlMediaType().withTypes(registeredTypes));

Obrigado

L

qual versão do restfulie vc tá usando?

G

A mais recente penso eu:

excerto do meu pom

<dependency>
       <groupId>br.com.caelum</groupId>
       <artifactId>restfulie</artifactId>
       <version>1.0.1</version>
</dependency>
L

a classe existe:

G

Lucas Cavalcanti:
a classe existe:
https://github.com/caelum/restfulie-java/blob/restfulie-1.0.1/client/src/main/java/br/com/caelum/restfulie/mediatype/JsonMediaType.java

certo, ate aqui estamos de acordo :slight_smile:

O método para registar as classes a serailizar é que não existe, neste caso o método withTypes() .

Já tentei estender o JsonMediaType para ter acesso ao metodo protected getTypesToEnhance() e assim adicionar as minhas classes mas mesmo assim nao tive sucesso. isto pq o metodo getTypesToEnhance() devolve uma view :frowning:

Tb tentei passar pelo construtor uma instância de DefaultEnhancer com as minhas classes lá registadas mas também não obtive sucesso.

Obrigado

L

usa

new JsonMediaType() {
       protected List<Class> getTypesToEnhance() {
		return Arrays.asList(Classe1.class, Classe2.class);
	}
}
G

Obrigado :slight_smile:
Parece funcionar dessa forma.

No entanto estou a verificar que o Restfulie não funciona bem com o json em termos de links.
A representação fica bem diferente, exemplo:

Entidade hypermediaEntry ( apenas contem relações )
XML:

<hypermediaEntry> <atom:link rel="empresa" href="http://localhost:8090/MobilOSServices/empresa/entry/" xmlns:atom="http://www.w3.org/2005/Atom"/> </hypermediaEntry>

JSON:

{ "hypermediaEntry": { "links": [ { "rel": "empresa", "href": "http://localhost:8090/MobilOSServices/empresa/entry/" } ] } }

Esta diferença resulta em com.thoughtworks.xstream.converters.ConversionException: links : links : links : links

Penso que li algures que nao era possivel usar esta faceta do restfulie ( relations ) com json, é verdade?

Obrigado :slight_smile:

G

Continuo a usar o restfulie mas estou a terminar o projeto e gostava de poder usar JSON em vez de xml…

Até agora nenhuma solução certo?

Criado 12 de setembro de 2013
Ultima resposta 23 de fev. de 2015
Respostas 18
Participantes 3