Estoy escribiendo un serializador para serializar POJO a JSON pero atorado en un problema de referencia circular. En la relación hibernate bidireccional de uno a muchos, el padre hace referencia a las referencias hijo e hijo de nuevo al padre y aquí mi serializador muere. (vea el código de ejemplo a continuación)
¿Cómo romper este ciclo? ¿Podemos obtener el árbol propietario de un objeto para ver si el objeto existe en algún lugar de su propia jerarquía de propietarios? ¿Alguna otra forma de encontrar si la referencia va a ser circular? o alguna otra idea para resolver este problema?¿Cómo resolver referencias circulares en el serializador json causado por el mapeo bidireccional de hibernación?
Respuesta
¿Puede una relación bi-direccional estar representada en JSON? Algunos formatos de datos no son buenos para algunos tipos de modelado de datos.
Un método para lidiar con ciclos cuando se trata de gráficos de objetos atravesados es hacer un seguimiento de los objetos que has visto hasta ahora (usando comparaciones de identidad), para evitar atravesar un ciclo infinito.
Hice lo mismo y funciona pero no estoy seguro si funcionará en todos los escenarios de mapeo. Al menos por ahora estoy bien configurado y seguiré pensando en tener una idea más elegante – WSK
Por supuesto que pueden, simplemente no hay un tipo de datos nativo o una estructura para esto. Pero cualquier cosa se puede representar en XML, JSON o en la mayoría de los demás formatos de datos. – StaxMan
Tengo curiosidad: ¿cómo representarías una referencia circular en JSON? –
que se basan en Google JSON Para hacer frente a este tipo de problema utilizando la función
Supongamos una relación bidireccional entre A y clase B de la siguiente
public class A implements Serializable {
private B b;
}
Y B
public class B implements Serializable {
private A a;
}
Ahora usa GsonBuilder Para obtener un objeto Gson encargo de la siguiente manera (Nótese setExclusionStrategies método)
Gson gson = new GsonBuilder()
.setExclusionStrategies(new ExclusionStrategy() {
public boolean shouldSkipClass(Class<?> clazz) {
return (clazz == B.class);
}
/**
* Custom field exclusion goes here
*/
public boolean shouldSkipField(FieldAttributes f) {
return false;
}
})
/**
* Use serializeNulls method if you want To serialize null values
* By default, Gson does not serialize null values
*/
.serializeNulls()
.create();
Ahora nuestra referencia circular
A a = new A();
B b = new B();
a.setB(b);
b.setA(a);
String json = gson.toJson(a);
System.out.println(json);
Tome un vistazo a GsonBuilder clase
Gracias Arthur por su amable sugerencia, pero la verdadera pregunta es cuál es la mejor manera de construir el llamado método genérico "shouldSkipClass". Por ahora trabajé en la idea de Matt y resolví mi problema, pero todavía escéptico, en el futuro esta solución puede romperse en ciertos escenarios. – WSK
Esto "resuelve" las referencias circulares eliminándolas. No hay forma de reconstruir la estructura de datos original desde el JSON generado. –
Jackson 1.6 (lanzado en septiembre de 2010) tiene soporte de anotación específico para mano ling tal vinculación padre/hijo, vea http://wiki.fasterxml.com/JacksonFeatureBiDirReferences. (Wayback Snapshot)
Por supuesto, ya puede excluir la serialización del enlace principal que ya utiliza la mayoría de los paquetes de procesamiento JSON (jackson, gson y flex-json al menos lo admiten), pero el verdadero truco está en cómo deserializarlo (re -crear enlace principal), no solo manejar el lado de serialización. Aunque parece que por ahora solo la exclusión podría funcionar para usted.
EDITAR (abril de 2012): Jackson 2.0 ahora es compatible con true identity references (Wayback Snapshot), por lo que puede resolverlo de esta manera también.
El enlace no funciona. – LOLKFC
cómo hacer que esto funcione cuando la dirección no es siempre la misma ... intenté poner ambas anotaciones en ambos campos pero no funcionó: Clase A { @JsonBackReference ("abc") @JsonManagedReference ("xyz") privado B b; } Clase B { @JsonManagedReference ("ABC") @JsonBackReference ("XYZ") privada A una; } – azi
Como se indicó anteriormente, Object Id (@JsonIdentityInfo) es la forma de hacer que las referencias generales funcionen. Las referencias Administradas/Atrás requieren ciertas instrucciones, por lo que no funcionarán para su caso. – StaxMan
Por ejemplo, ProductBean tiene serialBean. El mapeo sería una relación bidireccional. Si ahora tratamos de usar gson.toJson()
, terminará con referencia circular. Para evitar ese problema, puede seguir estos pasos:
- Recupere los resultados de la fuente de datos.
- Iterar la lista y asegúrese de que el serialBean no es nulo, y luego
- Establecer
productBean.serialBean.productBean = null;
- A continuación, intente utilizar
gson.toJson();
Eso debería resolver el problema
Al abordar este problema, Tomé el siguiente enfoque (estandarizando el proceso en mi aplicación, haciendo que el código sea claro y reutilizable):
- Crear una clase de anotación para ser utilizado en los campos desea excluidos
- definir una clase que implementa la interfaz ExclusionStrategy de Google
- Crear un método simple para generar el objeto GSON utilizando el GsonBuilder (similar a la explicación de Arthur)
- Anotar los campos que ser excluidos según sea necesario
- Aplicar las reglas de serialización a su objeto com.google.gson.Gson
- Serialice su objeto
Aquí está el código:
1)
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface GsonExclude {
}
2)
import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
public class GsonExclusionStrategy implements ExclusionStrategy{
private final Class<?> typeToExclude;
public GsonExclusionStrategy(Class<?> clazz){
this.typeToExclude = clazz;
}
@Override
public boolean shouldSkipClass(Class<?> clazz) {
return (this.typeToExclude != null && this.typeToExclude == clazz)
|| clazz.getAnnotation(GsonExclude.class) != null;
}
@Override
public boolean shouldSkipField(FieldAttributes f) {
return f.getAnnotation(GsonExclude.class) != null;
}
}
3)
static Gson createGsonFromBuilder(ExclusionStrategy exs){
GsonBuilder gsonbuilder = new GsonBuilder();
gsonbuilder.setExclusionStrategies(exs);
return gsonbuilder.serializeNulls().create();
}
4)
public class MyObjectToBeSerialized implements Serializable{
private static final long serialVersionID = 123L;
Integer serializeThis;
String serializeThisToo;
Date optionalSerialize;
@GsonExclude
@ManyToOne(fetch=FetchType.LAZY, optional=false)
@JoinColumn(name="refobj_id", insertable=false, updatable=false, nullable=false)
private MyObjectThatGetsCircular dontSerializeMe;
...GETTERS AND SETTERS...
}
5)
En el primer caso, nula se suministra al constructor, puede especificar otra clase que ser excluidos - se añaden las dos opciones a continuación
Gson gsonObj = createGsonFromBuilder(new GsonExclusionStrategy(null));
Gson _gsonObj = createGsonFromBuilder(new GsonExclusionStrategy(Date.class));
6)
MyObjectToBeSerialized _myobject = someMethodThatGetsMyObject();
String jsonRepresentation = gsonObj.toJson(_myobject);
o, para excluir el objeto Date
String jsonRepresentation = _gsonObj.toJson(_myobject);
esto evita que el objeto hijo sea procesado, puedo incluir el json hijo y luego detener el ciclo – ir2pid
Si está utilizando Javascript , hay una solución muy fácil para eso utilizando el parámetro replacer
del método JSON.stringify()
donde puede pasar una función para modificar el comportamiento de serialización predeterminado.
Así es como puede usarlo. Considere el siguiente ejemplo con 4 nodos en un gráfico cíclico.
// node constructor
function Node(key, value) {
this.name = key;
this.value = value;
this.next = null;
}
//create some nodes
var n1 = new Node("A", 1);
var n2 = new Node("B", 2);
var n3 = new Node("C", 3);
var n4 = new Node("D", 4);
// setup some cyclic references
n1.next = n2;
n2.next = n3;
n3.next = n4;
n4.next = n1;
function normalStringify(jsonObject) {
// this will generate an error when trying to serialize
// an object with cyclic references
console.log(JSON.stringify(jsonObject));
}
function cyclicStringify(jsonObject) {
// this will successfully serialize objects with cyclic
// references by supplying @name for an object already
// serialized instead of passing the actual object again,
// thus breaking the vicious circle :)
var alreadyVisited = [];
var serializedData = JSON.stringify(jsonObject, function(key, value) {
if (typeof value == "object") {
if (alreadyVisited.indexOf(value.name) >= 0) {
// do something other that putting the reference, like
// putting some name that you can use to build the
// reference again later, for eg.
return "@" + value.name;
}
alreadyVisited.push(value.name);
}
return value;
});
console.log(serializedData);
}
Más tarde, puede fácilmente volver a crear el objeto real con las referencias cíclicas mediante el análisis de los datos serializados y la modificación de la propiedad next
para que apunte al objeto real, si se trata de utilizar una referencia con nombre con un @
como en este ejemplo.
usa una solución similar a la de Arthur, pero en lugar de setExclusionStrategies
que utilizan
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
y usados @Expose
anotación GSON para los campos que necesito en el JSON, se excluyen otros campos.
Gracias amigo. Esto me funcionó. Después de una larga investigación, finalmente encontré la manera de salir de este error. – kepy97
la respuesta número 8 es la mejor, creo que si sabe qué campo está arrojando un error, solo establece el fild en nulo y resuelto.
List<RequestMessage> requestMessages = lazyLoadPaginated(first, pageSize, sortField, sortOrder, filters, joinWith);
for (RequestMessage requestMessage : requestMessages) {
Hibernate.initialize(requestMessage.getService());
Hibernate.initialize(requestMessage.getService().getGroupService());
Hibernate.initialize(requestMessage.getRequestMessageProfessionals());
for (RequestMessageProfessional rmp : requestMessage.getRequestMessageProfessionals()) {
Hibernate.initialize(rmp.getProfessional());
rmp.setRequestMessage(null); // **
}
}
Para hacer que el código legible por una gran comentario se mueve de la // **
comentario a continuación.
java.lang.StackOverflowError [El proceso de solicitud ha fallado; La excepción anidada es org.springframework.http.converter.HttpMessageNotWritableException: No se pudo escribir JSON: recursión infinita (StackOverflowError) (a través de la cadena de referencia: com.service.pegazo.bo.RequestMessageProfessional ["requestMessage"] -> com.service.pegazo. bo.RequestMessage [ ""] requestMessageProfessionals
No hay una "respuesta número 8", sería mejor dar el nombre del autor de la respuesta a la que se hace referencia. El texto que publicaste aquí no se puede leer, mira cómo se publican las respuestas y trata de diseñarlas correctamente. Finalmente, no entiendo cómo responde esto a la pregunta original. Por favor agregue más detalles para explicar la respuesta. – AdrianHHH
este error puede appened cuando se tienen dos objetos:
class object1{
private object2 o2;
}
class object2{
private object1 o1;
}
Con el uso de GSON para la serialización, tengo este error:
java.lang.IllegalStateException: circular reference error
Offending field: o1
Para resolver esto, sólo tiene que añadir la palabra clave transitoria:
class object1{
private object2 o2;
}
class object2{
transient private object1 o1;
}
Como se puede ver aquí: Why does Java have transient fields?
La palabra clave transitoria en Java se utiliza para indicar que un campo no debe ser serializado.
Así es como finalmente lo resolví en mi caso. Esto funciona al menos con Gson & Jackson.
private static final Gson gson = buildGson();
private static Gson buildGson() {
return new GsonBuilder().addSerializationExclusionStrategy(getExclusionStrategy()).create();
}
private static ExclusionStrategy getExclusionStrategy() {
ExclusionStrategy exlStrategy = new ExclusionStrategy() {
@Override
public boolean shouldSkipField(FieldAttributes fas) {
return (null != fas.getAnnotation(ManyToOne.class));
}
@Override
public boolean shouldSkipClass(Class<?> classO) {
return (null != classO.getAnnotation(ManyToOne.class));
}
};
return exlStrategy;
}
Si está utilizando Jackon para serializar, solo se aplica @JsonBackReference a su mapeo bi-directinal Se va a resolver el problema de referencia circular.
Nota: @JsonBackReference se utiliza para resolver el bucle infinito (StackOverflowError)
'@ JsonIgnore' hizo que mi' JpaRepository' fallara al mapear la propiedad, pero' @ JsonBackReference' resolvió la referencia circular y aún permitió el mapeo adecuado para el atributo problemático – Bramastic
- 1. ¿Cómo resolver referencias circulares en el serializador json causado por muchos TO Muchos hibernan mapeo bidireccional?
- 2. mapeo bidireccional de Python
- 3. Referencias circulares en Java
- 4. referencias circulares en RavenDB
- 5. referencias circulares en mi en Proyectos de
- 6. ¿Cómo tratar con referencias circulares?
- 7. Accesorios Doctrine - referencias circulares
- 8. Cómo resolver "borrar/modificar" conflicto causado por "escondite git aplica"
- 9. ¿Qué hay de malo en el mapeo bidireccional?
- 10. ¿Cómo eliminar referencias circulares en Entity Framework?
- 11. Creación de referencias genéricas circulares
- 12. ¿Cómo manejar referencias circulares con Autofac 2.4.5?
- 13. El serializador Newtonsoft json devuelve el objeto vacío
- 14. Referencias circulares en Javascript/Recolector de basura
- 15. Cómo guardar un objeto con referencias circulares?
- 16. WCF serializador personalizado
- 17. Uso Parcelable con referencias circulares
- 18. ¿Debería preocuparme por las referencias circulares en Python?
- 19. in_array en objetos con referencias circulares
- 20. ¿Las "referencias circulares" en JPA son antipatrón?
- 21. Referencias de archivos circulares no permitidas
- 22. Referencias circulares ¿Causa pérdida de memoria?
- 23. System.BadImageFormatException causado por el proyecto NUnit
- 24. ¿Es posible crear referencias circulares en Clojure?
- 25. javascript, referencias circulares y pérdidas de memoria
- 26. GSON - serializador personalizado en el caso específico
- 27. ¿Forma estándar de hacer referencia a un objeto por identidad (por ejemplo, para referencias circulares)?
- 28. cómo registrar un contador de referencias JSON personalizada en griales
- 29. Java detectar referencias circulares durante la clonación personalizada
- 30. análisis de JSON DateTime a partir de Newtonsoft JSON Serializador
Quizás quiso decir para pegar en algún código para nosotros para ayudar a resolver su problema? – Russell
La solución basada en anotaciones de eugene está bien, pero en este caso no es necesaria una anotación adicional ni la implementación de ExclusionStrategy. Simplemente use la palabra clave Java '[transient] (http://stackoverflow.com/questions/910374/why-does-java-have-transient-variables)' para eso. Funciona para la serialización de objetos Java estándar pero también [Gson lo respeta] (https://sites.google.com/site/gson/gson-user-guide#TOC-Java-Modifier-Exclusion). – MeTTeO