objectbox-java: Crash when JSON is being serialized to Entity

Basics

  • ObjectBox version (are using the latest version?): 1.0.0
  • Reproducibility: always

Reproducing the bug

Description

I’m getting some JSON from a REST call to a remote server and when the object is being created from the JSON object it crashes.

I have a just two entities for now, with a 1 to 1 relation. Each User entity has one UserSchoolDetails in it and all the information is passed in the JSON.

When I try to save the entry to the box it crashes.

Code

public static void updateUser() {
   //get the Box
   Box<User> userBox = SmartAlumni.boxStore.boxFor(User.class);
   
   //store the logged in User
    userBox.put(SmartAlumni.currentUser);
}
long __assignedId = collect313311(cursor, entity.id, PUT_FLAG_COMPLETE,
                __id10, wallet, 0, null,
                0, null, 0, null,
                __ID_userSchoolDetailsId, entity.userSchoolDetails.getTargetId(), __id8, __id8 != 0 ? dateOfBirth.getTime() : 0,
                __ID_is_admin, entity.getIs_admin() ? 1 : 0, 0, 0,
                0, 0, 0, 0,
                0, 0, 0, 0);

Logs & stackstraces

FATAL EXCEPTION: main
Process: com.pacent.smartalumni, PID: 21845
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Class java.lang.Object.getClass()' on a null object reference
at io.objectbox.relation.ToOne.getTargetIdField(ToOne.java:248)
at io.objectbox.relation.ToOne.getTargetId(ToOne.java:236)
at com.pacent.smartalumni.data.models.UserCursor.put(UserCursor.java:100)
at com.pacent.smartalumni.data.models.UserCursor.put(UserCursor.java:16)
at io.objectbox.Box.put(Box.java:380)
at com.pacent.smartalumni.utils.Utilities.updateUser(Utilities.java:56)
at com.pacent.smartalumni.presentation.signup.SignInActivity.onDataLoaded(SignInActivity.java:132)
at com.pacent.smartalumni.presentation.signup.SignInActivity.onDataLoaded(SignInActivity.java:34)
at com.pacent.smartalumni.data.remote.SignUpDataSource$1.onResponse(SignUpDataSource.java:37)
at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall$1$1.run(ExecutorCallAdapterFactory.java:70)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6077)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)

this is the JSON I get from the REST call

{"date_of_birth":"Aug 25, 2017 5:39:15 PM","email":"dami@kornetmail.com","first_name":"damilola","id":0,"is_admin":false,"last_name":"ola","name":"dami","phone_number":"1111111111","_id":"59a052b381ee2c1ae2effdd9","school_details":{"checkIdOfTargetForPut":false,"debugRelations":false,"resolvedTargetId":0,"targetId":0,"virtualProperty":false},"wallet":"59a6d36bda891ea63bb35d49"}

Entities

@Entity
public class User implements IUser {
    @Id
    public long id;

    @SerializedName("school_details")
    public ToOne<UserSchoolDetails> userSchoolDetails;
}
@Entity
public class UserSchoolDetails {
    @Id
    public long id;
}

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 16 (3 by maintainers)

Most upvoted comments

@jirevwe I just look into this issue and what i encountered is Retrofit2 is not aware of ToMany and ToOne type and also @greenrobot has already mentioned this thing in #104 .

I can share my model with the workaround. I am having a User Model which internally is related to Profile Picture Model coming from API Service and Location Model. I am using Retrofit2 to get the prepared User Model.

So my workaround in this issue is to provide @Transient annotation to ProfilePicture and Location type data members (as they will act as temporary data members and will not be stored into the db) and then add two new data members to the class as ToOne<ProfilePicture> and ToOne<Location> :

@Entity
public class User {
   @Id(assignable = true)
   @Expose
   @SerializedName("id")
   private long id;
   @Expose
   @Transient
   @SerializedName("location")
   private Location location;
   @Expose
   @SerializedName("age")
   private long age;
   @Expose
   @SerializedName("bio")
   private String bio;
   @Expose
   @SerializedName("dob")
   private String dob;
   @Expose
   @SerializedName("education")
   private String education;
   @Expose
   @SerializedName("email")
   private String email;
   @Expose
   @SerializedName("full_name")
   private String fullName;
   @Expose
   @SerializedName("gender")
   private String gender;
   @Expose
   @Transient
   @SerializedName("profile_picture")
   private ProfilePicture profilePicture;
   @Expose
   @SerializedName("username")
   private String username;
   @Expose
   @SerializedName("verified")
   private Boolean verified;
   @Expose
   @SerializedName("work")
   private String work;

   private ToOne<Location> locationRelation;
   private ToOne<ProfilePicture> profilePictureRelation;

   public long getId() {
       return id;
   }
   public Long getAge() {
       return age;
   }
   public String getBio() {
       return bio;
   }
   public String getDob() {
       return dob;
   }
   public String getEducation() {
       return education;
   }
   public String getEmail() {
       return email;
   }
   public String getFullName() {
       return fullName;
   }
   public String getGender() {
       return gender;
   }
   public String getUsername() {
       return username;
   }
   public Boolean getVerified() {
       return verified;
   }
   public String getWork() {
       return work;
   }
   public Location getLocation() {
       return location;
   }
   public ProfilePicture getProfilePicture() {
       return profilePicture;
   }
   public ToOne<ProfilePicture> getProfilePictureRelation() {
       return profilePictureRelation;
   }
   public ToOne<Location> getLocationRelation() {
       return locationRelation;
   }

   public void setId(long id) {
       this.id = id;
   }
   public void setAge(long age) {
       this.age = age;
   }
   public void setBio(String bio) {
       this.bio = bio;
   }
   public void setDob(String dob) {
       this.dob = dob;
   }
   public void setEducation(String education) {
       this.education = education;
   }
   public void setEmail(String email) {
       this.email = email;
   }
   public void setFullName(String fullName) {
       this.fullName = fullName;
   }
   public void setGender(String gender) {
       this.gender = gender;
   }
   public void setUsername(String username) {
       this.username = username;
   }
   public void setVerified(Boolean verified) {
       this.verified = verified;
   }
   public void setWork(String work) {
       this.work = work;
   }
   public void setLocation(Location location) {
       this.location = location;
   }
   public void setProfilePicture(ProfilePicture profilePicture) {
       this.profilePicture = profilePicture;
   }
   public void setLocationRelation() {
       locationRelation.setTarget(location);
   }
   public void setProfilePictureRelation() {
       profilePictureRelation.setTarget(profilePicture);
   }
}

Before storing the user model into the objectbox just call these two methods. As these methods will set the relation between user and the below models.

E.g.

private void OnSuccessFullySignedIn(SignUpSignInResponse signUpSignInResponse) {
       User user = signUpSignInResponse.getUser();
       user.setLocationRelation();
       user.setProfilePictureRelation();
       boxStore.boxFor(User.class).put(user);


      // Do the required after storing the data to ObjectBox.
   }

I hope this could resolve you problem as well.

Hmm… Nice workaround. Actually I already switched to another database. I can close this issue now then…

We’ll look into it later today.