[RESOLVIDO] Lista com JSON + Vraptor + Restfulie

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?

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

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

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

só se vc fizer direto o JSON:

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

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?

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

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)

br.com.rsyssoftwares.Categorias

existe essa classe na aplicação servidor?

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

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.

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

[quote=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.[/quote]

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

qual versão do restfulie vc tá usando?

A mais recente penso eu:

excerto do meu pom

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

a classe existe:

[quote=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[/quote]

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

usa

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

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:

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?