0

I'm having a problem de deserializing a class in Spring Boot. When my controller tries to deserialize it, it crashes. Here is the class:

@Entity
@Table(name="trash_cans")
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property="id")
public class TrashCan {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Column(name="TRASH_CAN_ID")
    long id;

    @Column(name="TRASH_CAN_NAME")
    String name;

    @ManyToOne
    @JoinColumn(name="PLACE_ID")
    private Place place;

    @OneToMany(mappedBy="trashCan", targetEntity=TrashMeter.class, fetch=FetchType.LAZY)
    private List<TrashCan> trashMeterList;

    @OneToMany(mappedBy="trashCan", targetEntity=TrashSensor.class, fetch=FetchType.LAZY)
    private List<TrashSensor> trashSensorList;

    public TrashCan() {     
    }

    public TrashCan(long id, String name) {
        super();
        this.id = id;
        this.name = name;
    }

    [getters and setters]
}

That depends on this one:

@Entity
@Table(name="trash_sensor")
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property="id")
public class TrashSensor {

    @Id
    @Column(name="id")
    @GeneratedValue(strategy=GenerationType.AUTO)
    private long id;

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

    @Column(name="description")
    private String description;

    @ManyToOne
    @JoinColumn(name="TRASH_CAN_ID")
    private TrashCan trashCan;

    @OneToMany(mappedBy = "trashSensor", targetEntity = Measurement.class, fetch = FetchType.LAZY)
    private List<Measurement> measurementList;

    public TrashSensor() {
        super();
    }

And Trash Sensor Depends on this Class:

@Entity
@Table(name="measurement")
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property="id")
public class Measurement {

    @Id
    @Column(name="id")
    @GeneratedValue(strategy=GenerationType.AUTO)
    private long id;

    @Column(name="value")
    private float value;

    @Column(name="last_measure")
    private LocalDateTime dateTime;

    @ManyToOne
    @JoinColumn(name="trash_sensor_id")
    private TrashCan trashSensor;

    public Measurement() {
    }

}

My Controler:

@RequestMapping(value="/trashCan", method=RequestMethod.GET)
    public ResponseEntity<Iterable<TrashCan>> getPlaces(){
        Iterable<TrashCan> trashCanIterable = trashCansRepository.findAll();

        return new ResponseEntity<>(trashCanIterable, HttpStatus.OK);
    }

When I call the webservice, I get this error:

Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException: Could not write content: could not deserialize (through reference chain: java.util.ArrayList[0]-br.com.simplepass.cleanerway.domain.TrashCan["trashSensorList"]-org.hibernate.collection.internal.PersistentBag[0]-br.com.simplepass.cleanerway.domain.TrashSensor["measurementList"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: could not deserialize (through reference chain: java.util.ArrayList[0]-br.com.simplepass.cleanerway.domain.TrashCan["trashSensorList"]-org.hibernate.collection.internal.PersistentBag[0]-br.com.simplepass.cleanerway.domain.TrashSensor["measurementList"])

I can't interpret this error =/. Any help with this problem greatly appreciated.

3 Answers 3

1

You are getting this error since your json is entering a loop, to avoid this, use @JsonIgnore annotation:

@OneToMany(mappedBy = "trashSensor", targetEntity = Measurement.class, fetch = FetchType.LAZY)
@JsonIgnore
private List<Measurement> measurementList;
6
  • And yes ...this is not the only place..you have to figure out more mapping which is entering into loop and add @JsonIgnore accordingly Commented Nov 4, 2016 at 12:42
  • Thanks for the fast reply! If I do that I won't be able to use the measurementList, right? I need this depency... so I can't use @JsonIgnore Commented Nov 4, 2016 at 12:42
  • then use @JsonIgnore on measurementList mapping Commented Nov 4, 2016 at 12:43
  • 1
    add the JsonIgnore in your second class and ignore the parent dependency
    – cralfaro
    Commented Nov 4, 2016 at 12:43
  • Orelse @JsonView is a better option where you can customize your Json according to your need Commented Nov 4, 2016 at 12:44
1

It happens when you use relations between entities. Imagine that your TrashCan has link to Trash in it. And your trash has link to it's wrapper - trashcan. So what you try to serialize TrashCan entity you also serializing Trash. And then when you are serializing trash trashcan is serialized again inside it. And so on. It's a loop. You can use @JsonIgnore on every entity that may cause loop.

@JsonIgnore
@ManyToOne
@JoinColumn(name="PLACE_ID")
private Place place;

@JsonIgnore
@OneToMany(mappedBy="trashCan", targetEntity=TrashMeter.class, fetch=FetchType.LAZY)
private List<TrashCan> trashMeterList;

@JsonIgnore
@OneToMany(mappedBy="trashCan", targetEntity=TrashSensor.class, fetch=FetchType.LAZY)
private List<TrashSensor> trashSensorList;

But it's a bad way. It's strongly recommended to use DTO (Data transfer object) pattern for you serialization/deserialization. It also gives you more flexibility. You can read about it here

2
  • Maksym, thanks for the reading, it's a really good idea. I need these lists, so I put the @JsonIgonre inside TrashCan and TrashSensor in the mapping object. Sorry, but I just can't see how the DTO is going to help me... The hole purpose of this controller is to get the data from the DB so, get it straight from the POJO looks really good in this situation. Anyway, the framework is pretty much doing everything. What do you think? Commented Nov 6, 2016 at 22:06
  • @LeandroBorgesFerreira , DTO is considered to be best practice. Serializing POJOs is a bad code. In your case, if your application is not so huge and it won't be bigger, you CAN serialize them. But think, if you would like to add your validation or you decide to provide not full entity somewhere else you will really NEED DTO to change your serialization for particular case in any place of your application. You left your full entity with all relations and just create and serialize special DTO as you wish for any controller, for example. Also it makes your model much cleaner in big applications.
    – maksyche
    Commented Nov 7, 2016 at 7:40
0

If you need trashMeterList and trashSensorList in response then follow this answer.

Due to hibernate lazy loading and no session while deserialisation, you are getting this exception.

To fix just change your controller:

    @RequestMapping(value="/trashCan", method=RequestMethod.GET)
    public ResponseEntity<Iterable<TrashCan>> getPlaces(){
        Iterable<TrashCan> trashCanIterable = trashCansRepository.findAll();
        List<TrashCan> responseList = new ArrayList<TrashCan>(trashCanIterable.size())
        while(trashCanIterable.hasNext()){
            TrashCan trashCan = trashCanIterable.next();
            for(TrashMeter trashMeter : trashCan.trashMeterList){

            }
            for(TrashSensor trashSensor : trashCan.trashSensorList){

            }
            responseList.add(trashCan);
        }
        return new ResponseEntity<>(responseList, HttpStatus.OK);
    }

Not the answer you're looking for? Browse other questions tagged or ask your own question.