Search code examples
javaspringjacksonself-reference

jackson deserialization with 2 fields self referencing the same class (self-reference cycle)


I have below class referencing to itself:

@Entity
@Inheritance(strategy = TABLE_PER_CLASS)
//@JsonIdentityInfo(property="rowId", generator = ObjectIdGenerators.PropertyGenerator.class)
public abstract class AbstractEntity implements Serializable {
    /**
     * 
     */
    private static final long serialVersionUID = 568799551343430329L;

    @OneToOne(optional=false, fetch=FetchType.EAGER)
    @JoinColumn(name="createdBy")
    protected User createdBy;
    @OneToOne(optional=false, fetch=FetchType.EAGER)
    @JoinColumn(name="lastUpdatedBy")
    protected User lastUpdatedBy;

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE)
    @Column(unique = true, nullable = false, updatable = false, length = 7)
    private Integer rowId;

    public User getCreatedBy() {
        return this.createdBy;
    }

    public void setCreatedBy(User createdBy) {
        this.createdBy = createdBy;
    }

    public User getLastUpdatedBy() {
        return this.lastUpdatedBy;
    }

    public void setLastUpdatedBy(User lastUpdatedBy) {
        this.lastUpdatedBy = lastUpdatedBy;
    }

    public Integer getRowId() {
        return this.rowId;
    }

    public void setRowId(Integer RowId) {
        this.rowId = RowId;
    }

    public String toString() {
        return "[Id]:" + this.rowId + " - [CreatedBy]:" + this.createdBy;
    }
}

Then I have a class User extending this class and a RepositoryUser interface:

public interface RepositoryUser extends CrudRepository<User, Integer> {

}

And a Controller:

@Controller
@RequestMapping(path = "/user")
public class ServiceUser {
    @Autowired
    private RepositoryUser repositoryUser;

    @GetMapping(path="/all", produces = "application/json; charset=UTF-8", headers = "Accept=application/json")
    public @ResponseBody Iterable<User> getAllUsers() {
        return repositoryUser.findAll();
    }

    @PostMapping(path="/add", consumes="application/json")
    public @ResponseBody User createOneUser(@RequestBody User user) {
        System.out.println(user);
        return repositoryUser.save(user);
    }
}

My issue is that I'm making reference to User twice (createdby and lastupdatedby) in the same class and either I tried JSonIdentityInfo, Jsonmanaged,jsonback nothing works. correctly.

I need to be able to have { User 1 data including created by and last updated by User 2 data including created by and last updated by }

and when I add I need to set the user who creates the record.

Can you please help me ?

Thanks a lot!


Solution

  • You could write/try a Custom Serializer Using StdSerializer.

    Example of CustomJsonSerializer. NB: Did not run the code.

    public class CustomJsonSerializer extends StdSerializer<AbstractEntity> {
    
      public CustomJsonSerializer() {
        this(null);
      }
    
      public CustomJsonSerializer(Class<AbstractEntity> t) {
        super(t);
      }
    
      @Override
      public void serialize(AbstractEntity value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException {
        Field[] fields = value.getClass().getDeclaredFields();
        jgen.writeStartObject();
    
        for (Field field : fields) {
            field.setAccessible(true);
    
           try {
               // Do the proper field mapping for field types . Object type example
               jgen.writeObjectField(field.getName(), field.get(value));
    
            } catch (Exception e) {
              // catch error
            }
        }
    
        jgen.writeEndObject();
      }
    }
    

    Then on your Rest Method use @JsonSerialize

    @JsonSerialize(using = CustomJsonSerializer.class)
    @GetMapping(path="/all", produces = "application/json; charset=UTF-8", headers = "Accept=application/json")
    public @ResponseBody Iterable<User> getAllUsers() {
        return repositoryUser.findAll();
    }
    

    Please see Custom Serializer And StdSerializer

    Possible different solution jackson-bidirectional infinite-recursion