Carlos
Carlos

Reputation: 303

Spring @JsonIgnore Serialization not working as expected

I have two Java classes: Usuario and Entrada.

Usuario:

@Entity
@Table(name="usuario")
@NamedQuery(name="Usuario.findAll", query="SELECT u FROM Usuario u")
public class Usuario implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private int idusuario;

    private String apellido1;

    private String apellido2;

    private String email;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name="fecha_alta")
    private Date fechaAlta;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name="fecha_baja")
    private Date fechaBaja;

    @Column(name="is_admin")
    private boolean isAdmin;

    private String nombre;

    private String password;

    //bi-directional many-to-one association to CompraEntrada
    @OneToMany(mappedBy="usuarioBean")
    @JsonIgnore
    private List<CompraEntrada> compraEntradas;

    //bi-directional many-to-one association to Entrada
    @OneToMany(mappedBy="usuarioBean")
    private List<Entrada> entradas;

    //bi-directional many-to-one association to Evento
    @OneToMany(mappedBy="usuario")
    @JsonIgnore
    private List<Evento> eventos;

    //bi-directional many-to-one association to Mensaje
    @OneToMany(mappedBy="emisor")
    @JsonIgnore
    private List<Mensaje> mensajesEmitidos;

    //bi-directional many-to-one association to Mensaje
    @OneToMany(mappedBy="receptor")
    @JsonIgnore
    private List<Mensaje> mensajesRecibidos;

Entrada:

@Entity
@Table(name="entrada")
@NamedQuery(name="Entrada.findAll", query="SELECT e FROM Entrada e")
public class Entrada implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private int identrada;

    //bi-directional many-to-one association to Evento
    @ManyToOne
    @JoinColumn(name="evento")
    private Evento eventoBean;

    //bi-directional many-to-one association to Usuario
    @ManyToOne
    @JoinColumn(name="usuario")
    @JsonIgnore
    private Usuario usuarioBean;

I have also the class Evento:

@Entity
@Table(name="evento")
@NamedQuery(name="Evento.findAll", query="SELECT e FROM Evento e")
public class Evento implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private int idevento;

    private String categoria;

    private String descripcion;

    @Column(name="entradas_disponibles")
    private int entradasDisponibles;

    @Column(name="entradas_venta")
    private int entradasVenta;

    private int estado;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name="fecha_hora")
    private Date fechaHora;

    private String imagen;

    private String localizacion;

    @Column(name="precio_entrada")
    private double precioEntrada;

    private String titulo;

    //bi-directional many-to-one association to CompraEntrada
    @OneToMany(mappedBy="eventoBean")
    private List<CompraEntrada> compraEntradas;

    //bi-directional many-to-one association to Entrada
    @OneToMany(mappedBy="eventoBean")
    private List<Entrada> entradas;

    //bi-directional many-to-one association to Usuario
    @ManyToOne
    @JoinColumn(name="creador")
    private Usuario usuario;

I'm using Spring Data but I have a recursive serialization problem. When I "print" an object of type Usuario, it should serialize all the information of Usuario. The point here is that, Entrada (List in Usuario) has an attribute of type Usuario.

I don't want to serialize the Usuario of Entrada. I'm just looking to serialize each one of the items in List of Usuario.

I've tried to add @JsonIgnore in the attribute of type Usuario in Entrada, but I still have the same problem.

The log shows:

2017-12-04 18:57:46.980[0;39m [31mERROR[0;39m [35m3389[0;39m [2m---[0;39m [2m[nio-8080-exec-1][0;39m [36mo.a.c.c.C.[.[.[/].[dispatcherServlet]   [0;39m [2m:[0;39m Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageNotWritableException: Could not write content: Infinite recursion (StackOverflowError) (through reference chain: es.softcorp.domains.Entrada["eventoBean"]->es.softcorp.domains.Evento["entradas"]->org.hibernate.collection.internal.PersistentBag[0]->es.softcorp.domains.Entrada["eventoBean"]->es.softcorp.domains.Evento["entradas"]->org.hibernate.collection.internal.PersistentBag[0]->es.softcorp.domains.Entrada["eventoBean"]->es.softcorp.domains.Evento["entradas"]-

and so on

Upvotes: 1

Views: 2403

Answers (1)

Zeronex
Zeronex

Reputation: 434

I have solved this, from what I can remember for @JsonIgnore to work you have to disable auto field detection for that class or if you wish for all classes.

There is two ways you can solve the recursion issue.

  1. Json annotations to tell ObjectMapper about the bi-directional relation.
  2. @JsonIgnore, @JsonView the field thats causing the recursion/loop.

For a sample project I did disabled auto field detection for all classes like this and used @JsonProperty and @JsonIgnore in classes I wanted to serialise. If auto detect is disabled you have to manually mark the fields.

/**
 * Disable auto field detection for JSON message converter.
 * @return
 */
@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
    MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
    ObjectMapper objectMapper = new ObjectMapper();

    objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

    objectMapper.setVisibility(
            objectMapper
            .getVisibilityChecker()
            .with(Visibility.NONE)
    );

    jsonConverter.setObjectMapper(objectMapper);
    return jsonConverter;
}

There is also @JsonView annotation which work similar to @JsonIgnore and can be used to select which fields will get serialised, here is the link for further reading https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring

The correct way to solve your issue is to tell the ObjectMapper about the recursive/bi-directional relation as explained on this page http://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion

In summary you use @JsonManagedReference on the child ( non owning side) and use @JsonBackReference on parent (owning side of relation).

Upvotes: 1

Related Questions