title: Servlet Gson().toJson infinite loop
body: <p>I am having some trouble in the servlet wherein everytime i change the option in a dropdown menu,
  a different value will be passed to the servlet and then it results into an infinite loop. When I am not changing the option (no change in value) in the dropdown, there is no error.</p>
  
  <p>Here is mycode:</p>
  
  <p>My Javascript:</p>
  
  <pre><code>&lt;script&gt;
  
  function loadStaff(){
  //dropdown
  var positionDropDown = document.getElementById("positionsDropdown");
  //value of the drop down
  var positionID = positionDropDown.options[positionDropDown.selectedIndex].value;
  
      $.getJSON('loadStaff?positionID=' + positionID, function(data) {
              -- no populate code yet
  });
  }
  &lt;/script&gt;
  </code></pre>
  
  <p>My AjaxServlet:</p>
  
  <pre><code>protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  
        String userPath = request.getServletPath();
  
      if (userPath.equals("/loadStaff")) {
  
          String positionID = request.getParameter("positionID");
          Position position = positionFacade.find(Integer.parseInt(positionID));
          Collection staffCollection = position.getStaffCollection();
          List&lt;Staff&gt; staffList = new ArrayList(staffCollection);
  
          String staffListJson = new Gson().toJson(staffList);
          response.setContentType("application/json");
          response.setCharacterEncoding("UTF-8");
          response.getWriter().write(staffListJson);
  
      }
  
  }
  </code></pre>
  
  <p>Upon debuggig. The error comes at line:</p>
  
  <pre><code>String staffListJson = new Gson().toJson(staffList);
  </code></pre>
  
  <p>Output error:</p>
  
  <pre><code>&gt; INFO:   WebModule[null] ServletContext.log(): The server side
  &gt; component of the HTTP Monitor has detected a
  &gt; java.lang.StackOverflowError. This happens when there is an infinite
  &gt; loop in the web module. Correct the cause of the infinite loop before
  &gt; running the web module again.
  &gt; 
  &gt; INFO:   The server side component of the HTTP Monitor has detected a
  &gt; java.lang.StackOverflowError. This happens when there is an infinite
  &gt; loop in the web module. Correct the cause of the infinite loop before
  &gt; running the web module again. WARNING:  
  &gt; StandardWrapperValve[AjaxServlet]: Servlet.service() for servlet
  &gt; AjaxServlet threw exception java.lang.StackOverflowError
  
  &gt; WARNING:   StandardWrapperValve[AjaxServlet]: Servlet.service() for
  &gt; servlet AjaxServlet threw exception java.lang.StackOverflowError  at
  &gt; sun.util.calendar.ZoneInfo.getOffsets(ZoneInfo.java:248)  at
  &gt; java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2276)
  &gt;   at
  &gt; java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2248)
  &gt;   at java.util.Calendar.setTimeInMillis(Calendar.java:1140)   at
  &gt; java.util.Calendar.setTime(Calendar.java:1106)    at
  &gt; java.text.SimpleDateFormat.format(SimpleDateFormat.java:955)  at
  &gt; java.text.SimpleDateFormat.format(SimpleDateFormat.java:948)  at
  &gt; java.text.DateFormat.format(DateFormat.java:336)  at
  &gt; com.google.gson.internal.bind.DateTypeAdapter.write(DateTypeAdapter.java:90)
  &gt;   at
  &gt; com.google.gson.internal.bind.DateTypeAdapter.write(DateTypeAdapter.java:41)
  &gt;   at
  &gt; com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
  &gt;   at
  &gt; com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:89)
  &gt;   at
  &gt; com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:195)
  &gt;   at com.google.gson.Gson$FutureTypeAdapter.write(Gson.java:892)  at
  &gt; com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
  &gt;   at
  &gt; com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:89)
  &gt;   at
  &gt; com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:195)
  &gt;   at
  &gt; com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
  &gt;   at
  &gt; com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:89)
  &gt;   at
  &gt; com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:195)
  &gt;   at com.google.gson.Gson$FutureTypeAdapter.write(Gson.java:892)  at
  &gt; com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
  &gt;   at
  &gt; com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:89)
  &gt;   at
  &gt; com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:195)
  &gt;   at
  &gt; com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
  &gt;   at
  &gt; com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:89)
  &gt;   at
  &gt; com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:195)
  &gt;   at com.google.gson.Gson$FutureTypeAdapter.write(Gson.java:892)
  </code></pre>
  
  <p>I also noticed that this traces are just repeating output of stacktrace;</p>
  
  <p><strong>EDIT:</strong>
  Staff class</p>
  
  <pre><code>@Entity
  public class Staff implements Serializable {
      private static final long serialVersionUID = 1L;
      @Id
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      @Basic(optional = false)
      @Column(name = "id")
      private Integer id;
      @Basic(optional = false)
      @NotNull
      @Size(min = 1, max = 45)
      @Column(name = "last_name")
      private String lastName;
      @Basic(optional = false)
      @NotNull
      @Size(min = 1, max = 45)
      @Column(name = "first_name")
      private String firstName;
      @Size(max = 45)
      @Column(name = "middle_name")
      private String middleName;
      @Basic(optional = false)
      @NotNull
      @Size(min = 1, max = 6)
      @Column(name = "gender")
      private String gender;
      @Basic(optional = false)
      @NotNull
      @Column(name = "date_of_birth")
      @Temporal(TemporalType.DATE)
      private Date dateOfBirth;
      @Basic(optional = false)
      @NotNull
      @Size(min = 1, max = 45)
      @Column(name = "nationality")
      private String nationality;
      @Basic(optional = false)
      @NotNull
      @Column(name = "date_hired")
      @Temporal(TemporalType.TIMESTAMP)
      private Date dateHired;
      @Size(max = 20)
      @Column(name = "status")
      private String status;
      @OneToMany(cascade = CascadeType.ALL, mappedBy = "staff")
      private Collection&lt;StaffApointments&gt; staffApointmentsCollection;
      @OneToOne(cascade = CascadeType.ALL, mappedBy = "staff")
      private StaffContact staffContact;
      @JoinColumn(name = "account_id", referencedColumnName = "id")
      @ManyToOne(optional = false)
      private Account accountId;
      @JoinColumn(name = "position_id", referencedColumnName = "id")
      @ManyToOne(optional = false)
      private Position positionId;
  
      public Staff() {
      }
  
      public Staff(Integer id) {
          this.id = id;
      }
  
      public Staff(Integer id, String lastName, String firstName, String gender, Date dateOfBirth, String nationality, Date dateHired) {
          this.id = id;
          this.lastName = lastName;
          this.firstName = firstName;
          this.gender = gender;
          this.dateOfBirth = dateOfBirth;
          this.nationality = nationality;
          this.dateHired = dateHired;
      }
  
      public Integer getId() {
          return id;
      }
  
      public void setId(Integer id) {
          this.id = id;
      }
  
      public String getLastName() {
          return lastName;
      }
  
      public void setLastName(String lastName) {
          this.lastName = lastName;
      }
  
      public String getFirstName() {
          return firstName;
      }
  
      public void setFirstName(String firstName) {
          this.firstName = firstName;
      }
  
      public String getMiddleName() {
          return middleName;
      }
  
      public void setMiddleName(String middleName) {
          this.middleName = middleName;
      }
  
      public String getGender() {
          return gender;
      }
  
      public void setGender(String gender) {
          this.gender = gender;
      }
  
      public Date getDateOfBirth() {
          return dateOfBirth;
      }
  
      public void setDateOfBirth(Date dateOfBirth) {
          this.dateOfBirth = dateOfBirth;
      }
  
      public String getNationality() {
          return nationality;
      }
  
      public void setNationality(String nationality) {
          this.nationality = nationality;
      }
  
      public Date getDateHired() {
          return dateHired;
      }
  
      public void setDateHired(Date dateHired) {
          this.dateHired = dateHired;
      }
  
      public String getStatus() {
          return status;
      }
  
      public void setStatus(String status) {
          this.status = status;
      }
  
      @XmlTransient
      public Collection&lt;StaffApointments&gt; getStaffApointmentsCollection() {
          return staffApointmentsCollection;
      }
  
      public void setStaffApointmentsCollection(Collection&lt;StaffApointments&gt; staffApointmentsCollection) {
          this.staffApointmentsCollection = staffApointmentsCollection;
      }
  
      public StaffContact getStaffContact() {
          return staffContact;
      }
  
      public void setStaffContact(StaffContact staffContact) {
          this.staffContact = staffContact;
      }
  
      public Account getAccountId() {
          return accountId;
      }
  
      public void setAccountId(Account accountId) {
          this.accountId = accountId;
      }
  
      public Position getPositionId() {
          return positionId;
      }
  
      public void setPositionId(Position positionId) {
          this.positionId = positionId;
      }
  
      @Override
      public int hashCode() {
          int hash = 0;
          hash += (id != null ? id.hashCode() : 0);
          return hash;
      }
  
      @Override
      public boolean equals(Object object) {
          // TODO: Warning - this method won't work in the case the id fields are not set
          if (!(object instanceof Staff)) {
              return false;
          }
          Staff other = (Staff) object;
          if ((this.id == null &amp;&amp; other.id != null) || (this.id != null &amp;&amp; !this.id.equals(other.id))) {
              return false;
          }
          return true;
      }
  
      @Override
      public String toString() {
          return "entity.Staff[ id=" + id + " ]";
      }
  
  }
  </code></pre>
  
  <p>Here is the Position class as well:</p>
  
  <pre><code>@Entity
  public class Position implements Serializable {
      private static final long serialVersionUID = 1L;
      @Id
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      @Basic(optional = false)
      @Column(name = "id")
      private Integer id;
      @Basic(optional = false)
      @NotNull
      @Size(min = 1, max = 45)
      @Column(name = "name")
      private String name;
      @OneToMany(cascade = CascadeType.ALL, mappedBy = "positionId")
      private Collection&lt;Staff&gt; staffCollection;
  
      public Position() {
      }
  
      public Position(Integer id) {
          this.id = id;
      }
  
      public Position(Integer id, String name) {
          this.id = id;
          this.name = name;
      }
  
      public Integer getId() {
          return id;
      }
  
      public void setId(Integer id) {
          this.id = id;
      }
  
      public String getName() {
          return name;
      }
  
      public void setName(String name) {
          this.name = name;
      }
  
      @XmlTransient
      public Collection&lt;Staff&gt; getStaffCollection() {
          return staffCollection;
      }
  
      public void setStaffCollection(Collection&lt;Staff&gt; staffCollection) {
          this.staffCollection = staffCollection;
      }
  
      @Override
      public int hashCode() {
          int hash = 0;
          hash += (id != null ? id.hashCode() : 0);
          return hash;
      }
  
      @Override
      public boolean equals(Object object) {
          // TODO: Warning - this method won't work in the case the id fields are not set
          if (!(object instanceof Position)) {
              return false;
          }
          Position other = (Position) object;
          if ((this.id == null &amp;&amp; other.id != null) || (this.id != null &amp;&amp; !this.id.equals(other.id))) {
              return false;
          }
          return true;
      }
  
      @Override
      public String toString() {
          return "entity.Position[ id=" + id + " ]";
      }
  
  }
  </code></pre>
  
  <p><strong>EDIT 2:</strong></p>
  
  <p>I added @expose to the attributes in my staff class except for staffCollection. But I still have 1 problem. Everytime the 1st value in the dropdown is selected (value=1) it still gives the infinite loop error. Can anyone help me?</p>
  
  <p><strong>EDIT 3:</strong></p>
  
  <p>Fixed it! I added final GsonBuilder builder = new GsonBuilder();
              builder.excludeFieldsWithoutExposeAnnotation(); as a whole. It now works</p>