Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

'@JsonIgnore' annotation not working with creator properties, serialization #1317

Closed
rajanya opened this issue Jul 30, 2016 · 20 comments
Closed

Comments

@rajanya
Copy link

rajanya commented Jul 30, 2016

My code having @JsonIgnore annotation is working fine with version 2.4.4 but it stopped working when I upgraded to version 2.8.1. I am getting "Infinite recursion (StackOverflowError)" only with 2.8.1 version.

@cowtowncoder
Copy link
Member

@rajanya I am not aware of any intended changes, so I would need a reproduction (ideally simple unit test) to show what kind of usage is negative affected.

@rajanya
Copy link
Author

rajanya commented Jul 31, 2016

I am building a project using Spring Boot and JPA+Hibernate. This is an example of JPA many-to-many mapping using Sprint Boot and MySQL. I have used Lombok for getter and setter.

public class Song {

         @Id
         @Column(name = "id", nullable = false)
         private Integer id;

         @OneToMany(mappedBy = "song", cascade = CascadeType.PERSIST)
         private Set<SongArtist> artists;
 }

 public class Artist {

         @Id
         @Column(name = "id", nullable = false)
         private Integer id;

         @OneToMany(mappedBy = "artist")
         @JsonIgnore
         private Set<SongArtist> songs;
}

public class SongArtist implements Serializable{

        private static final long serialVersionUID = 8058391821299774421L;

        @Id
        @ManyToOne
        @JoinColumn(name = "song_id")
        @JsonIgnore
        private Song song;

        @Id 
        @ManyToOne
        @JoinColumn(name = "artist_id")
        private Artist artist;
}

@Repository
public interface SongRepository extends CrudRepository<Song,Integer>{
}

The caller:

 songRepository.findAll();

Error with Jackson 2.8.1: (This error is not coming with Jackson 2.4.4)

  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:
          com.pitchhigh.BEService.model.Song["artists"]->
          org.hibernate.collection.internal.PersistentSet[0]->
          com.pitchhigh.BEService.model.SongArtist["song"]->
          com.pitchhigh.BEService.model.Song["artists"]->
          org.hibernate.collection.internal.PersistentSet[0]->
          com.pitchhigh.BEService.model.SongArtist["song"]->
          com.pitchhigh.BEService.model.Song["artists"]->
          .....

Please let me know if the example is clear and sufficient.

@cowtowncoder
Copy link
Member

Unfortunately that is not quite enough to reproduce the problem; especially if jackson-datatype-hibernate is being used in addition to core databind. Further, Spring and Lombok may be involved.

So I would really need a stand-alone reproduction; POJO declaration looks ok to me, and basic ignoral handling has no known problems at this point.

@clubshrimp
Copy link

I have the same problem when upgrading from 2.6.3 to version 2.7.0 and upwards (tested up to 2.8.2).
Everything works fine with 2.6.3. I am using Tomcat, Hibernate and Lombok as well.

@cowtowncoder
Copy link
Member

@clubshrimp Nonetheless I can not reproduce this without an actual example, ideally without Lombok or Hibernate. If Hibernate is required part, bug needs to be filed for jackson-datatype-hibernate since Hibernate proxying is typically involved. As to Lombok, reproduction must include generated class since Jackson has no special support for Lombok so everything is based on just class definition and annotations.

@clubshrimp
Copy link

clubshrimp commented Sep 2, 2016

Yes, I can imagine it's hard to reproduce without an actual example. Sorry for that. I don't know Jackson well enough to figure out how to write a stand-alone reproduction, but if I figure it out, I will certainly post it here!
I actually don't use the jackson-datatype-hibernate module. But I forgot to say I use jersey-media-json-jackson. The only think I am sure of is that everything works fine if I use 2.6.3 or lower.

@clubshrimp
Copy link

I just tested with 2.6.7, and it works fine as well, so the issue appeared between 2.6.7 and 2.7.0.

@cowtowncoder
Copy link
Member

@clubshrimp My guess is that this could be related to addition of handling of @ConstructorProperties in Jackson 2.7; something that formerly required use of jackson-datatype-jdk7. It has been mentioned that there are cases where configuration to prevent generation of annotation are needed -- not sure if that is the issue here or not, but could be one work around.

Note on Hibernate: the original report definitely is related to Hibernate, since PersistentSet specifically is included in stack trace. So it is also possible that your problem could be different. Including stack trace might be helpful.

@clubshrimp
Copy link

clubshrimp commented Sep 3, 2016

My report is also related to Hibernate; I was just saying that I didn't use jackson-datatype-hibernate module (it didn't seem necessary)

I haven't used @ConstructorProperties in my code, and didn't have jackson-datatype-jdk7 either.
The modules related to jackson that I have in my POM are:

  • jackson-core
  • jackson-databind
  • jackson-annotations
  • jackson-datatype-joda
  • jersey-media-json-jackson

The trace is

> org.glassfish.jersey.server.internal.process.MappableException: 
com.fasterxml.jackson.databind.JsonMappingException: 
Infinite recursion (StackOverflowError) (through reference chain: 
forex.common.entity.Currency["countries"]->
org.hibernate.collection.internal.PersistentSet[0]->
forex.common.entity.Country["currency"]->
forex.common.entity.Currency["countries"]->
org.hibernate.collection.internal.PersistentSet[0]->

The currency class is:

import com.fasterxml.jackson.annotation.JsonIgnore;
import forex.trading.entity.Position;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import java.util.HashSet;
import java.util.Set;

@Entity
@NoArgsConstructor
@Builder
@AllArgsConstructor
@EqualsAndHashCode(of = {"id", "tradingActive"})
@ToString(includeFieldNames = false, of = {"id", "tradingActive"})
@Table(catalog = "fxcommon")
@Getter
@Setter

public class Currency {

    @Id
    @Column(name = "Code", nullable = false, length = 3)
    private String id;

    @JsonIgnore
    @Column(name = "TradingActive")
    private boolean tradingActive;

    @JsonIgnore
    @OneToMany(mappedBy = "currency")
    @Cascade(CascadeType.ALL) 
    private Set<Country> countries = new HashSet<>();

    @JsonIgnore
    @OneToMany(mappedBy = "baseCurrency")
    private Set<Position> positionsBase;

    @JsonIgnore
    @OneToMany(mappedBy = "quoteCurrency")
    private Set<Position> positionsQuote;

    public Currency(String idParam, boolean tradingActiveParam) {
        id = idParam;
        tradingActive = tradingActiveParam;
    }
}

@cowtowncoder
Copy link
Member

@clubshrimp Hibernate usage is often crucial, although question is whether it is required or not. More often than not it is, esp. so if hibernate module is not used: Hibernate uses proxy types (PersistentSet) and it tends to cause problems above and beyond basic JDK collections.
Lombok can also add its challenges, and combination is a royal PITA to support.

At any rate, what I would need to work on this would be a reproduction:

  1. Ideally without either Lombok or Hibernate, to show a problem in core databind: if so, issue belongs here
  2. With Hibernate, but without Lombok, adding classes as post-processed by Lombok (if that's how it works -- I don't use Lombok myself). If so, issue should be moved (re-created) to jackson-datatype-hibernate

I am guessing (2) is the most likely route.

@clubshrimp
Copy link

Good point, I should have used jackson-datatype-hibernate from the beginning even if I had no problem without it until 2.7.
I have been doing tests both with and without jackson-datatype-hibernate5. With that module, I don't have the exception anymore in 2.7.2 :-) However, the behaviour with @JsonIgnore still seems strange.

  • v2.6.7, I don't see "countries" in the json; which is expected as I have @JsonIgnore for that field.
  • v2.7.2 (same in v2.8.2), the json includes "countries": null. With @JsonIgnore. I would expect not to see the countries property at all (which is not null by the way)
    Same with PositionsBase and PositionsQuote which also have @JsonIgnore

I haven't seen any reference in the javadoc for @JsonIgnore whether the property should actually be present in the Json but with a null value. I think "ignored" means it should not be there.

If that is not normal as I suppose, I need to see if I can run the test without lombok on the Currency class...

@dsulimchuk
Copy link

As a workaround you can use @JsonIgnoreProperties instead @JsonIgnore. That works for me))

@cowtowncoder cowtowncoder changed the title '@JsonIgnore' annotation not working (2.8.1) '@JsonIgnore' annotation not working (2.8.1) with Hibernate Sep 30, 2016
@kajo-bellabeat
Copy link

kajo-bellabeat commented Oct 20, 2016

@cowtowncoder I recently ran into the same issue when upgrading to new version (same persisten bag stack overflow exception like in first post). After some digging I've managed to pinpoint the issue to @AllArgsConstructor usage, specificlly its usage of @ConstructorProperties. Here is a failing test that replicates this issue:

import java.beans.ConstructorProperties;

import org.junit.Assert;
import org.junit.Test;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class JacksonTest {

    @Test
    public void testThatJsonIgnoreWorksWithConstructorProperties() throws JsonProcessingException {
        Testing testing = new Testing("shouldBeIgnored", "notIgnore");
        ObjectMapper om = new ObjectMapper();
        String json = om.writeValueAsString(testing);
        System.out.println(json);
        Assert.assertFalse(json.contains("shouldBeIgnored"));
    }

    public class Testing {

        @JsonIgnore
        private String ignore;

        private String notIgnore;

        public Testing() {}

        @ConstructorProperties({"ignore", "notIgnore"})
        public Testing(String ignore, String notIgnore) {
            super();
            this.ignore = ignore;
            this.notIgnore = notIgnore;
        }

        public String getIgnore() {
            return ignore;
        }

        public void setIgnore(String ignore) {
            this.ignore = ignore;
        }

        public String getNotIgnore() {
            return notIgnore;
        }

        public void setNotIgnore(String notIgnore) {
            this.notIgnore = notIgnore;
        }
    }
}

If @ConstructorProperties is removed, test passes.

I've also went back through jackson versions and noticed that this issue started happening in 2.7.0. version. Prior to this version, the test above passes

@cowtowncoder
Copy link
Member

@kajo-bellabeat Hmmh. That is tricky, as this would become split-annotation case... due to @ConstructorProperties implying inclusion, @JsonIgnore exclusion. I suspect adding @JsonIgnore to getIgnore() should remove serialization; otherwise it would be visible and thereby associated with constructor parameter.

That is to say, I am not 100% sure behavior is incorrect in that regard, considering semantic of @ConstructorProperties (pre-2.7 same would have been achieved by incuding jackson-module-jdk7).

@tarelli
Copy link

tarelli commented Dec 17, 2016

Experiencing the same problem after updating from 2.5.3 to 2.8.5. We use spring framework. @JsonIgnore and @JsonIgnoreProperties are not working anymore.

@tarelli
Copy link

tarelli commented Dec 17, 2016

@cowtowncoder do you have any ideas? This is the file and the annotations that were working prior to moving to 2.8.5 after which parentProject is now serialised resulting in a SOE. I also tried removing the parameter from the constructor but it doesn't make a difference. Thanks!

Update: looks like it's a conflict since Spring 3.1.3 still uses an older version of fasterxml which is causing the annotations to be ignored now.

Fixed forcing Spring to use Fasterxml 2.

@cowtowncoder
Copy link
Member

@tarelli At this point I am only going to look into this if there is a self-contained unit test that does not rely on external dependencies (like Spring or Hibernate or Lombok). In case of code generation (like Lombok), that would need to include generated classes.

Note, too, that if you say there's a StackOverflowException (or any exception for that matter), it'd be good idea to include (parts of) stack trace. Just input file, or verbal description, does not allow much troubleshooting.

@cowtowncoder
Copy link
Member

Looks like test by @kajo-bellabeat might work, so let me have another look here.

@cowtowncoder
Copy link
Member

Ok. I can reproduce the issue, and looking into it, I think the problem is that property collector considers this a "split" case (one where ignoral AND inclusion are specified via annotations). This should not occur, in a way, since individual properties are NOT explicitly included but as a set. I'll see if I can figure out how to fix this case.

cowtowncoder added a commit that referenced this issue Jan 16, 2017
@cowtowncoder cowtowncoder changed the title '@JsonIgnore' annotation not working (2.8.1) with Hibernate '@JsonIgnore' annotation not working with creator properties, serialization Jan 17, 2017
@jpfss
Copy link

jpfss commented Jun 20, 2019

It happens the same problem with "spring-boot-starter-data-jpa". The version as follow:

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
   <version>2.1.5.RELEASE</version>
</dependency>

It happens cause by the “@RestController” and "@responsebody" annotion In SpringMVC. This problem causes some problems In My solution as follow:
Problem 1,Infinite recursion。
Problem 2, Stack overflow。
Problem3,If the object relationship in Jpa is too complicated, a lot of data will be queried at one time resulting in "java.lang.OutOfMemoryError: Java heap space".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants