Jackson with Spring MVC duplicate nested objects without deserializing

I am trying to convert the following POJO to JSON in @RestController

:

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

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private int id;

    private String addr1;

    private String addr2;

    private String landmark;

    private BigDecimal lat;

    private BigDecimal lng;

    private String zipcode;

    //bi-directional many-to-one association to City
    @ManyToOne
    private City city;

    //bi-directional many-to-one association to State
    @ManyToOne
    private State state;

    public UserLocation() {
    }

    //Getter - Setters

}

      

The nested City.java is as follows:

@Entity
@NamedQuery(name="City.findAll", query="SELECT c FROM City c")
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property="@id", scope = City.class)
public class City implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private int id;

    private String name;

    //bi-directional many-to-one association to State
    @ManyToOne
    @JsonIgnore
    private State state;

    //bi-directional many-to-one association to UserLocation
    @OneToMany(mappedBy="city")
    @JsonIgnore
    private List<UserLocation> userLocations;

    public City() {
    }

    public int getId() {
        return this.id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @JsonProperty("state")
    public State getState() {
        return this.state;
    }

    public void setState(State state) {
        this.state = state;
    }


    public List<UserLocation> getUserLocations() {
        return this.userLocations;
    }

    public void setUserLocations(List<UserLocation> userLocations) {
        this.userLocations = userLocations;
    }

    public UserLocation addUserLocation(UserLocation userLocation) {
        getUserLocations().add(userLocation);
        userLocation.setCity(this);

        return userLocation;
    }

    public UserLocation removeUserLocation(UserLocation userLocation) {
        getUserLocations().remove(userLocation);
        userLocation.setCity(null);

        return userLocation;
    }

}

      

Another nested State.java class looks like this:

@Entity
@NamedQuery(name="State.findAll", query="SELECT s FROM State s")
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property="@id", scope = State.class)
public class State implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private int id;

    private String name;

    //bi-directional many-to-one association to City
    @OneToMany(mappedBy="state")
    @JsonIgnore
    private List<City> cities;

    //bi-directional many-to-one association to UserLocation
    @OneToMany(mappedBy="state")
    @JsonIgnore
    private List<UserLocation> userLocations;

    public State() {
    }

    public int getId() {
        return this.id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<City> getCities() {
        return this.cities;
    }

    public void setCities(List<City> cities) {
        this.cities = cities;
    }

    public City addCity(City city) {
        getCities().add(city);
        city.setState(this);

        return city;
    }

    public City removeCity(City city) {
        getCities().remove(city);
        city.setState(null);

        return city;
    }

    public List<UserLocation> getUserLocations() {
        return this.userLocations;
    }

    public void setUserLocations(List<UserLocation> userLocations) {
        this.userLocations = userLocations;
    }

    public UserLocation addUserLocation(UserLocation userLocation) {
        getUserLocations().add(userLocation);
        userLocation.setState(this);

        return userLocation;
    }

    public UserLocation removeUserLocation(UserLocation userLocation) {
        getUserLocations().remove(userLocation);
        userLocation.setState(null);

        return userLocation;
    }

}

      

JSON is converted from UserLocation.java in the following order:

{
    id: 1,
    addr1: "11905 Technology",
    addr2: "Eden Prairie",
    landmark: null,
    lat: null,
    lng: null,
    zipcode: "55344",
    city: {
        @id: 1,
        id: 2,
        name: "Westborough",
        state: {
            @id: 1,
            id: 2,
            name: "MA"
        }
    },
    state: 1
}

      

As you can see, the object State

comes as a whole object inside city

. But external State

(property "UserLocation is showing just an id of

State object. I need to have a same

state object as that of

city" instead of a simple id.

I am relatively new to the JackSon api. Please advice which approach should I follow to achieve this requirement.

thank

+3


source to share


2 answers


So Jackson developed the JsonIdentityInfo annotation logic.

 * Annotation used for indicating that values of annotated type
 * or property should be serializing so that instances either
 * contain additional object identifier (in addition actual object
 * properties), or as a reference that consists of an object id
 * that refers to a full serialization. In practice this is done
 * by serializing the first instance as full object and object
 * identity, and other references to the object as reference values.

      

Jackson will run full serialization the first time, and only the ID will be serialized the second time he finds this object.

So, there are two ways how you can fix this:

1) you can just remove the @JsonIdentityInfo annotation and Jackson will serialize the object as you expected, but it will remove the @id field from the answer. This is probably good, because you will still have an id property.

2) I feel like you can just rebuild your objects and remove some references. I would say it is good to do it anyway. First of all, you can remove the state link from UserLocation. I would argue that there is no need to have a state in the userLocation class due to the state being attached to a city. By doing this, you will gain access to the State from the city and your problem will be solved. Also I would remove the reference to the userLocations list from the City class as well as from the State class.



It will look like this:

UserLocation is city and stateless.

The city has state and no userLocations

The state has no user places, nor cities.

Hope it helps

+5


source


First remove the annotations from your State.java and City.java

@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property="@id", scope = State.class)

@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property="@id", scope = City.class)

      



No need for these annotations, and in RestController add return type as @ResponseBody UserLocation. This will give you the json of this class.

0


source







All Articles