VRaptor - XStream CircularReferenceException

26 respostas
yorgan

Olá pessoal,
Estou com um problema para serializar minhas entidades no formato JSON.

Controller

this.result.use(json()).indented().from(usuario).exclude("usuarioRegraList").serialize();

Entity Usuario:

@Entity
public class Usuario extends br.com.k2studio.util.jpa.entity.BaseEntity implements Serializable {
	private static final long serialVersionUID = 1L;

	@Id
	@SequenceGenerator(name="USUARIO_ID_GENERATOR", sequenceName="USUARIO_ID_SEQ", allocationSize=1)
	@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="USUARIO_ID_GENERATOR")
	private Integer id;
	
	private String nome;

	@Column(name="chave_usuario")
	private String chaveUsuario;

	private String email;

	private String senha;
	
	@ManyToOne(cascade={CascadeType.PERSIST})
	private Endereco endereco;
	
	@Enumerated(EnumType.ORDINAL)
	private Status status;
	
	@Column(name="data_cadastro")
	private Timestamp dataCadastro;
	
	@Transient
	private String confirmacao;
	
	@OneToMany(mappedBy="usuario", cascade={CascadeType.PERSIST})
	private List<UsuarioRegra> usuarioRegraList;

        //Getters/Setters

}

Entity UsuarioRegra:

@Entity
@Table(name="usuario_regra")
public class UsuarioRegra extends br.com.k2studio.util.jpa.entity.BaseEntity implements Serializable {
	private static final long serialVersionUID = 1L;

	@Id
	@SequenceGenerator(name="USUARIO_REGRA_ID_GENERATOR", sequenceName="USUARIO_REGRA_ID_SEQ", allocationSize=1)
	@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="USUARIO_REGRA_ID_GENERATOR")
	private Integer id;

	@ManyToOne(cascade={CascadeType.PERSIST})
	private Usuario usuario;

	//bi-directional many-to-one association to Regra
        @ManyToOne(cascade={CascadeType.PERSIST})
	private Regra regra;

        //Getters/Setters

}

Stacktrace:

22:29:48,183 ERROR [org.apache.catalina.core.ContainerBase.[jboss.web].[localhost].[/app].[default]] Servlet.service() for servlet default threw exception: br.com.caelum.vraptor.InterceptionException: exception raised, check root cause for details: com.thoughtworks.xstream.core.TreeMarshaller$CircularReferenceException: 
	at br.com.caelum.vraptor.interceptor.ExecuteMethodInterceptor.intercept(ExecuteMethodInterceptor.java:96) [:]
	at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54) [:]
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54) [:]
	at br.com.caelum.vraptor.core.LazyInterceptorHandler.execute(LazyInterceptorHandler.java:61) [:]
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54) [:]
	at br.com.caelum.vraptor.interceptor.ParametersInstantiatorInterceptor.intercept(ParametersInstantiatorInterceptor.java:89) [:]
	at br.com.caelum.vraptor.core.LazyInterceptorHandler.execute(LazyInterceptorHandler.java:59) [:]
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54) [:]
	at br.com.caelum.vraptor.interceptor.FlashInterceptor.intercept(FlashInterceptor.java:83) [:]
	at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54) [:]
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54) [:]
	at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:56) [:]
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54) [:]
	at br.com.caelum.vraptor.interceptor.InstantiateInterceptor.intercept(InstantiateInterceptor.java:48) [:]
	at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54) [:]
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54) [:]
	at br.com.caelum.vraptor.interceptor.ExceptionHandlerInterceptor.intercept(ExceptionHandlerInterceptor.java:71) [:]
	at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54) [:]
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54) [:]
	at br.com.caelum.vraptor.interceptor.ResourceLookupInterceptor.intercept(ResourceLookupInterceptor.java:69) [:]
	at br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54) [:]
	at br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:54) [:]
	at br.com.caelum.vraptor.core.EnhancedRequestExecution.execute(EnhancedRequestExecution.java:23) [:]
	at br.com.caelum.vraptor.VRaptor$1.insideRequest(VRaptor.java:92) [:]
	at br.com.caelum.vraptor.ioc.spring.SpringProvider.provideForRequest(SpringProvider.java:58) [:]
	at br.com.caelum.vraptor.VRaptor.doFilter(VRaptor.java:89) [:]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:274) [:6.0.0.Final]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:242) [:6.0.0.Final]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:343) [:]
	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:109) [:]
	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83) [:]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355) [:]
	at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:97) [:]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355) [:]
	at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:100) [:]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355) [:]
	at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:78) [:]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355) [:]
	at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:54) [:]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355) [:]
	at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:35) [:]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355) [:]
	at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter(BasicAuthenticationFilter.java:177) [:]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355) [:]
	at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:188) [:]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355) [:]
	at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105) [:]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355) [:]
	at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:79) [:]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355) [:]
	at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:149) [:]
	at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:237) [:3.0.3.RELEASE]
	at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167) [:3.0.3.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:274) [:6.0.0.Final]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:242) [:6.0.0.Final]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:275) [:6.0.0.Final]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191) [:6.0.0.Final]
	at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:181) [:6.0.0.Final]
	at org.jboss.modcluster.catalina.CatalinaContext$RequestListenerValve.event(CatalinaContext.java:285) [:1.1.0.Final]
	at org.jboss.modcluster.catalina.CatalinaContext$RequestListenerValve.invoke(CatalinaContext.java:261) [:1.1.0.Final]
	at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:88) [:6.0.0.Final]
	at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.invoke(SecurityContextEstablishmentValve.java:100) [:6.0.0.Final]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127) [:6.0.0.Final]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) [:6.0.0.Final]
	at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:158) [:6.0.0.Final]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) [:6.0.0.Final]
	at org.jboss.web.tomcat.service.request.ActiveRequestResponseCacheValve.invoke(ActiveRequestResponseCacheValve.java:53) [:6.0.0.Final]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:362) [:6.0.0.Final]
	at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:877) [:6.0.0.Final]
	at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:654) [:6.0.0.Final]
	at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:951) [:6.0.0.Final]
	at java.lang.Thread.run(Thread.java:680) [:1.6.0_22]
Caused by: com.thoughtworks.xstream.core.TreeMarshaller$CircularReferenceException: 
	at com.thoughtworks.xstream.core.TreeMarshaller.convert(TreeMarshaller.java:83) [:]
	at com.thoughtworks.xstream.core.TreeMarshaller.convertAnother(TreeMarshaller.java:78) [:]
	at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.marshallField(AbstractReflectionConverter.java:157) [:]
	at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter$2.writeField(AbstractReflectionConverter.java:148) [:]
	at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter$2.visit(AbstractReflectionConverter.java:118) [:]
	at com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider.visitSerializableFields(PureJavaReflectionProvider.java:129) [:]
	at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.doMarshal(AbstractReflectionConverter.java:100) [:]
	at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.marshal(AbstractReflectionConverter.java:58) [:]
	at com.thoughtworks.xstream.core.TreeMarshaller.convert(TreeMarshaller.java:86) [:]
	at com.thoughtworks.xstream.core.TreeMarshaller.convertAnother(TreeMarshaller.java:78) [:]
	at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.marshallField(AbstractReflectionConverter.java:157) [:]
	at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter$2.writeField(AbstractReflectionConverter.java:148) [:]
	at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter$2.visit(AbstractReflectionConverter.java:118) [:]
	at com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider.visitSerializableFields(PureJavaReflectionProvider.java:129) [:]
	at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.doMarshal(AbstractReflectionConverter.java:100) [:]
	at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.marshal(AbstractReflectionConverter.java:58) [:]
	at com.thoughtworks.xstream.core.TreeMarshaller.convert(TreeMarshaller.java:86) [:]
	at com.thoughtworks.xstream.core.TreeMarshaller.convertAnother(TreeMarshaller.java:78) [:]
	at com.thoughtworks.xstream.core.TreeMarshaller.convertAnother(TreeMarshaller.java:63) [:]
	at com.thoughtworks.xstream.core.TreeMarshaller.start(TreeMarshaller.java:98) [:]
	at com.thoughtworks.xstream.core.AbstractTreeMarshallingStrategy.marshal(AbstractTreeMarshallingStrategy.java:38) [:]
	at com.thoughtworks.xstream.XStream.marshal(XStream.java:837) [:]
	at com.thoughtworks.xstream.XStream.marshal(XStream.java:826) [:]
	at com.thoughtworks.xstream.XStream.toXML(XStream.java:801) [:]
	at br.com.caelum.vraptor.serialization.xstream.XStreamSerializer.serialize(XStreamSerializer.java:237) [:]
	at br.com.k2studio.climp.web.util.json.JsonUtilImpl$1.serialize(JsonUtilImpl.java:66) [:]
	at br.com.k2studio.climp.web.controller.LoginController.doLogin(LoginController.java:45) [:]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) [:1.6.0_22]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) [:1.6.0_22]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) [:1.6.0_22]
	at java.lang.reflect.Method.invoke(Method.java:597) [:1.6.0_22]
	at br.com.caelum.vraptor.interceptor.ExecuteMethodInterceptor.intercept(ExecuteMethodInterceptor.java:61) [:]
	... 71 more

[]'s
Daniel

26 Respostas

Lucas_Cavalcanti

olá yorgan,

vc fez alguma customização do json()? se tirar o indented funciona?

por padrão vc não precisaria do exclude, pq o VRaptor só serializa primitivos (string, numeros e datas) por padrão

o que tem no baseEntity?

yorgan

Não alterei nada no json().
E mesmo sem o indented o erro persiste.
Agora, quanto a serializar somente tipos primitivos, daí já não concordo. Se eu tiver um arraylist de String dentro do objeto, ele será serializado também, assim como uma lista contendo qualquer tipo de objeto.
E a BaseEntity é uma classe abstrata que contém basicamente get e set do atributo id.

[]'s
Daniel

Lucas_Cavalcanti

ele não deveria serializar listas a menos que vc adicione-a com include()

não tem nenhum customJSONSerialization ou coisa do tipo no projeto?

yorgan

Criei uma interface e classe, mas o código que postei não utilizam ela.
De qualquer forma, seguem os arquivos:

Interface JsonUtil:

import br.com.caelum.vraptor.serialization.JSONSerialization;

public interface JsonUtil extends JSONSerialization {
	
	public JsonUtilImpl success(final Boolean success);
	
	public JsonUtilImpl redirect(final String url);
	
	public JsonUtilImpl selected(final Object value);
}

Classe JsonUtilImpl:

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;

import javax.servlet.http.HttpServletResponse;

import br.com.caelum.vraptor.interceptor.TypeNameExtractor;
import br.com.caelum.vraptor.ioc.Component;
import br.com.caelum.vraptor.serialization.ProxyInitializer;
import br.com.caelum.vraptor.serialization.SerializerBuilder;
import br.com.caelum.vraptor.serialization.xstream.XStreamJSONSerialization;
import br.com.caelum.vraptor.serialization.xstream.XStreamSerializer;
import br.com.caelum.vraptor.view.ResultException;

@Component
public class JsonUtilImpl extends XStreamJSONSerialization implements JsonUtil {

	private final HttpServletResponse response;
	private final TypeNameExtractor extractor;
	private final ProxyInitializer initializer;
	
	private final String SUCCESS   = "\"success\": @value, \n";
	private final String REDIRECT  = "\"redirect\": \"@value\", \n";
	private final String SELECTED  = "\"selected\": @value, \n";

	private StringBuilder content;
	
	public JsonUtilImpl(HttpServletResponse response, TypeNameExtractor extractor, ProxyInitializer initializer) {
		super(response, extractor, initializer);
		this.response = response;
		this.extractor = extractor;
		this.initializer = initializer;
	}
	
	public JsonUtilImpl success(final Boolean success) {
		getContent().append(SUCCESS.replace("@value", success.toString()));
		getSerializer();
		return this;
	}
	
	public JsonUtilImpl redirect(final String url) {
		getContent().append(REDIRECT.replace("@value", url));
		getSerializer();
		return this;
	}
	
	public JsonUtilImpl selected(final Object value) {
		if(isNumeric(value.getClass())) {
			getContent().append(SELECTED.replace("@value", value.toString()));
		} else {
			getContent().append(SELECTED.replace("@value", "\"" +  value.toString() + "\""));
		}
		getSerializer();
		return this;
	}
	
	protected SerializerBuilder getSerializer() {
		try {
			final PrintWriter writer = response.getWriter();
			final StringWriter out = new StringWriter();
			return new XStreamSerializer(getXStream(), new PrintWriter(out), extractor, initializer) {
				@Override
				public void serialize() {
					super.serialize();
					writer.append(out.getBuffer().replace(0, 1, content.toString()));
					writer.close();
				}
			};
		} catch (IOException e) {
			throw new ResultException("Unable to serialize data", e);
		}
	}
	
	private StringBuilder getContent() {
		if(this.content == null) {
			this.content = new StringBuilder();
			this.content.append("{");
		}
		return this.content;
	}
	
	private boolean isNumeric(Class clazz) {
		return Integer.class.equals(clazz) 
			|| Double.class.equals(clazz) 
			|| Long.class.equals(clazz)
			|| Number.class.equals(clazz);
	}
	
}
yorgan

Para essa interface a chamada ficaria assim:

this.result.use(JsonUtil.class).success(true).redirect("/app/secure/home").selected("1").indented().from(usuario).serialize();

Mas o erro é o mesmo.

Então para tratar isso eu criei uma classe Helper:

import java.lang.reflect.Field;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import net.vidageek.mirror.dsl.Mirror;

public class CollectionHelper {
	
	public static List clean(List list) {
		return clean(list, new String[] {});
	}
	
	public static List clean(List list, String ... exclude) {
		int i = 0;
		for(Object entity : list) {
			list.set(i, clean(entity, exclude));
			i++;
		}
		return list;
	}
	
	public static Object clean(Object entity) {
		return clean(entity, new String[] {});
	}
	
	public static Object clean(Object entity, String ... exclude) {
		Mirror mirror = new Mirror();
		for (Field field : new Mirror().on(entity.getClass()).reflectAll().fields()) {
			if (!isPrimitive(field.getType()) && exclude(field, exclude)) {
				mirror.on(entity).set().field(field).withValue(null);
			}
		}
		
		return entity;
	}
	
	private static boolean exclude(Field field, String[] list) {
		for(String param : list) {
			if(field.getName().equals(param)) { 
				return false;
			}
		}
		return true;
	}

	private static boolean isPrimitive(Class<?> type) {
		return type.isPrimitive()
			|| type.isEnum()
			|| Number.class.isAssignableFrom(type)
			|| type.equals(String.class)
			|| Date.class.isAssignableFrom(type)
			|| Calendar.class.isAssignableFrom(type)
			|| Boolean.class.equals(type)
			|| Character.class.equals(type);
	}

}

Chamada:

usuario = (Usuario) CollectionHelper.clean(usuario);
Lucas_Cavalcanti

sua classe JsonUtil extends JSONSerialization…

adivinha qual é a classe que o Results.json() retorna :wink:

então toda vez que vc usa json() o VRaptor vai procurar uma implementação de JSONSerialization e usar a sua, a JsonUtilImpl

o estranho é que vc não chama o recursive(), nem nada do tipo, então o VRaptor deveria usar o comportamento padrão. Você consegue reproduzir esse bug no blank-project? Faz isso por favor?

PS: algum motivo pra chamar o getSerializer() em vários métodos?

yorgan

Viajei, ele não serializa mesmo as listas.
Fiz o seguinte teste e funcionou:

@Path("/json")
	public void teste() {
		Usuario usuario = new Usuario();
		usuario.setId(1);
		usuario.setChaveUsuario("121324343werrsfd");
		usuario.setUsuarioRegraList(new ArrayList<UsuarioRegra>());
		UsuarioRegra ur = new UsuarioRegra();
		ur.setId(1);
		ur.setUsuario(usuario);
		usuario.getUsuarioRegraList().add(ur);
		this.result.use(JsonUtil.class).success(true).redirect("/app/secure/home").selected("1").indented().from(usuario).include("usuarioRegraList").serialize();
	}

O resultado foi:

{"success": true, 
"redirect": "/app/secure/home", 
"selected": "1", 
"usuario": {
  "id": 1,
  "chaveUsuario": "121324343werrsfd",
  "usuarioRegraList": [
    {
      "id": 1
    }
  ]
}}

Será que a maneira que o JPA preenche o objeto ou o Lazy não são o problema?
Que essas listas vindas do banco ficam como PersistentBag, não é?

yorgan

Tenho sempre o getSerializer() porque nem sempre faço uso de todos os métodos ao utilizar essa classe.

Lucas_Cavalcanti

o ProxyInitializer deveria cuidar desse problema…

debuga e coloca um breakpoint na linha do result.use(json()) e dá um inspect no usuario (ctrl+shift+I)

tira um print e me manda o resultado desse inspect, por favor?

yorgan

Print

Lucas_Cavalcanti

o usuario é um Usuario ou um proxy?

yorgan

Mas estou utilizando EJB3 no JBoss 6, então no controller já não existe mais sessão do JPA para fazer o load.

Lucas_Cavalcanti

ele deveria ignorar a lista por padrão, algo tá fazendo com que ela serialize.

precisava tentar isolar esse bug mesmo…

se vc tirar o @Component do seu JsonUtilImpl e usar result.use(json())… funciona?

yorgan

Retirei como você pediu, mas o erro persiste.
Para confiar limpei o usuario assim:

usuario = (Usuario) CollectionHelper.clean(usuario);
this.result.use(json()).indented().from(usuario).exclude("usuarioRegraList").serialize();

Dessa forma a serialização ocorreu corretamente.

E para garantir limpei e passando como parâmetro a lista usuarioRegraList, para garantir que qualquer outra coisa fosse removida.

usuario = (Usuario) CollectionHelper.clean(usuario,  "usuarioRegraList");
this.result.use(json()).indented().from(usuario).exclude("usuarioRegraList").serialize();

Mas dessa forma o erro persistiu.

yorgan

Eu ainda acredito que o XStream não enxergue o PersistentBag como coleção, e sim como um objeto. E por isso que ele entra na referência cirular.
Tanto que, se a lista for populada manualmente, como no exemplo que postei acima, o erro não ocorre.

Lucas_Cavalcanti

precisava tentar isolar o problema mesmo. Não consigo ver motivos pra essa circular reference…

consegue reproduzir isso no blank-project?

yorgan

Bom, sono foi embora mesmo…vou tentar fazer isso agora.

Lucas_Cavalcanti

blz, valeu =)

yorgan

Criei um projeto em branco, criei uma base com as tabelas usuario, regra e usuario_regra e fiz um findById via JPA para testar a serialização.
Funcionou para o seguinte código:

Usuario usuario = new Usuario();
usuario.setId(1);
usuario = dao.selectById(usuario);
result.use(json()).indented().from(usuario).serialize();

Já se eu adicionar a coleção via include o erro de CircularReference aparece:

Usuario usuario = new Usuario();
usuario.setId(1);
usuario = dao.selectById(usuario);
result.use(json()).indented().from(usuario).include("usuarioRegraList").serialize();

Então deve ser alguma coisa nas minhas classes mesmo.
Amanhã continuo os testes.

Projeto que fiz os testes pode ser baixado aqui: http://k2cloud.net/vraptor-teste.zip
*Projeto p/ Eclipse e JBoss 6

Lucas_Cavalcanti

tenta adicionar esse converter no XStream:

xstream.registerConverter(new CollectionConverter() {
    @Override
    public boolean canConvert(Class type) {
         return Collection.class.isAssignableFrom(type); //pode deixar só o persistentBag tb
    }

    @Override
    public void marshall(Object obj, HSW writer, MC ctx) {
         Collection collection = (Collection) obj;
         super.marshall(new ArrayList(collection), writer, ctx);
    }
});

o XStream funciona muito bem com ArrayList, talvez isso resolva o problema.

Abraços

fabianorodrigo

Oi pessoal,

Vocês conseguiram resolver/entender o problema que vocês relataram nessa thread? É que estou tendo um problema muito parecido.
Tenho um modelo idêntico, uma classe DEPARTAMENTO com um relacionamento OneToMany com a REUNIAO.
Mas quando chamo esse meu código de serialização tudo funciona com esperado, ou seja, a List não é serializada e nem recebo exceção:

@Path("/depto/lista2.json")
	public void lista2Json(){
        result.use(Results.json()).withoutRoot().from(dao.selectByNamedQuery("selectDepartamentos")).serialize();
    }

Mas como estou usando o jqGrid na interface, eu resolvi criar uma classe pra encapsular meu resultado acrescentando outros campos esperados pelo plugin do jQuery.
Eis abaixo a dita cuja e em seguida o meu método do controller que gera a exceção CircularReferenceException. Se tiverem alguma dica, eu agradeço.
Valeu!

public class jqGridJSONObj {
	
	private int page;
	private int total;
	private int records;
	
	@SuppressWarnings("unchecked")
	private List rows;
	
	public int getPage() {
		return page;
	}
	public void setPage(int page) {
		this.page = page;
	}
	public int getTotal() {
		return total;
	}
	public void setTotal(int total) {
		this.total = total;
	}
	public int getRecords() {
		return records;
	}
	public void setRecords(int records) {
		this.records = records;
	}
	@SuppressWarnings("unchecked")
	public List getRows() {
		return rows;
	}
	@SuppressWarnings("unchecked")
	public void setRows(List rows) {
		this.rows = rows;
	}
}

O método do controller:

@Path("/depto/lista.json") public void listaJson(){ List<BaseEntity> departamentos = dao.selectByNamedQuery("selectDepartamentos"); jqGridJSONObj dto = new jqGridJSONObj(); dto.setPage(1); dto.setTotal(departamentos.size()); dto.setRecords(departamentos.size()); dto.setRows(departamentos); result.use(Results.json()).withoutRoot().from(dto).include("rows").exclude("reunioes").serialize(); }
PS: Com ou sem o exclude(“reunioes”) a exceção é a mesma. E o resultado que aparece no browser é esse:
{“page”: 1,“total”: 1,“records”: 1,“rows”: [{“id”: 1,“nome”: “Financeiro”,“logradouro”: “Rua xxx”,“numero”: “1”,“complemento”: “”,“bairro”: “xxxxx”,“cidade”: “Rio de Janeiro”,“uf”: “RJ”,“pais”: “Brasil”,“reunioes”: [false,{"@class": “departamento”

Lucas_Cavalcanti

dá um exclude(“rows.reunioes”)

fabianorodrigo

The same exception, no donuts for you :o)

@Path("/depto/lista.json") public void listaJson(){ List<BaseEntity> departamentos = dao.selectByNamedQuery("selectDepartamentos"); jqGridJSONObj dto = new jqGridJSONObj(); dto.setPage(1); dto.setTotal(departamentos.size()); dto.setRecords(departamentos.size()); dto.setRows(departamentos); result.use(Results.json()).withoutRoot().from(dto).include("rows").exclude("rows.reunioes").serialize(); }

Uma coisa estranha que eu achei no Stacktrace é que em uma das linhas da pilha é do XStream.toXML(), normal?

at com.thoughtworks.xstream.core.AbstractTreeMarshallingStrategy.marshal(AbstractTreeMarshallingStrategy.java:38)
at com.thoughtworks.xstream.XStream.marshal(XStream.java:837)
at com.thoughtworks.xstream.XStream.marshal(XStream.java:826)

at com.thoughtworks.xstream.XStream.toXML(XStream.java:801)

Obrigado

Lucas_Cavalcanti

muito estranho… ele não deveria nem tentar serializar a lista de reuniões, listas são excluidas por padrão…

tenta fazer um teste só pra esse caso:

troque o List rows para List

PS: o toXML gera um json, dependendo da configuração :wink:

fabianorodrigo

Na mosca, Lucas.
Só pra registrar, antes de tentar com a classe List eu tentei com um List, sendo BaseEntity uma classe abstrata da qual Departamento herda, mas deu o mesmo problema de referência circular.
Tem alguma explicação pra esse comportamento?

Obrigado

Lucas_Cavalcanti

tem, o VRaptor só consegue descobrir o tipo que está declarado no field da classe jqGridJSONObj. Não tem como saber qual é o tipo genérico da instancia de List (isso é apagado em tempo de execução)

então o VRaptor não consegue descobrir qual é o tipo certo, e excluir os campos

Criado 14 de fevereiro de 2011
Ultima resposta 19 de mai. de 2011
Respostas 26
Participantes 3