Work at SourceForge, help us to make it a better place! We have an immediate need for a Support Technician in our San Francisco or Denver office.

Close

Deserialization failure - complex object

Help
Jay
2012-12-17
2013-03-19
  • Jay
    Jay
    2012-12-17

    I have following example json string tht I'm trying to deserialize using FlexJson.

    {
    "status" : {"id" : 0,"code" : "Finalized"},
    "classDate" : "20121213",
    "creationDate" : "20121213",
    "updateDate" : "20121213",
    "schoolYear" : {"year" : "2012","id" : 25 }
    "courseType" : {"id" : 12,"name" : "course 1"},
    "instructor" : {"name" : "TEST TNSTR","id" : 11,"activeIndicator" : 1},
    "school" : {"id" : 11,"code" : "0100","name" : "School 1"},
    "attendees" : [
    {
      "status" : { "id" : 0, "code" : "Active" }, "updateDate" : "20121213", "testScore" : 0, "student" : { "id" : 1, "district" : { "id" : 1, "name" : "test county" }, "city" : "city", "name" : "new test", "address" : "123 main st", "stateCode" : "ST", "class" : { "id" : 11, "name" : "class 1" }, "zip" : "12345", "number" : "123456789", "dateOfBirth" : "19891213" }
    }
    ],
    }
    

    I'm trying to deserialize into an JPA entity instance, TrainingClass with getter/setter (removed for abbreviation) for follwoing properties. Complex types in turn are more JPA entities.

    public class TrainingClass implements Serializable {
    private long id;
    private TrainingClassStatus status;
    private Date classDate;
    private Date creationDate;
    private Date updateDate;
    private SchoolYear schoolYear;
    private CourseType courseType;
    private Instructor instructor;
    private School school;
    private Set<ClassAttendee> attendees = new HashSet<ClassAttendee>();
    }
    

    Serializing to json works fine but no luck with deserialization.

    I've tried without hint,

    JSONDeserializer<TrainingClass> deserializer = new JSONDeserializer<TrainingClass>().use(null, TrainingClass.class).use(Date.class, transformer);
    TrainingClass training = deserializer.deserialize(json);
    

    And with specific targets,

    DateTransformer transformer = new DateTransformer( "yyyyMMdd" );
    JSONDeserializer<TrainingClass> deserializer = new JSONDeserializer<TrainingClass>()
                .use(null, TrainingClass.class)
                .use("classDate",transformer)
                .use("creationDate", transformer)
                .use("updateDate", transformer)
                .use("school", School.class)
                .use("schoolYear", SchoolYear.class)
                .use("instructor", Instructor.class)
                .use("instructor.activeIndicator", Boolean.class)
                .use("courseType", CourseType.class)
                .use("status", TrainingClassStatus.class)
                .use( "attendees", ArrayList.class )
                .use("attendees.values", ClassAttendee.class)
                .use("attendees.values.creationDate", transformer)
                .use("attendees.values.updateDate", transformer)
                .use("attendees.values.status", ClassAttendeeStatus.class)
                .use("attendees.values.student", Student.class)
                .use("attendees.values.student.dateOfBirth", transformer)
                .use("attendees.values.student.district", Distict.class)
                .use("attendees.values.student.class", StudentClass.class)
                .use("attendees.values.student.status", StudentStatus.class);
    TrainingClass training =deserializer.deserialize(json);
    

    I see it is getting parsed into HashMap but not able to bind to Object, throws exception.

    java.lang.IllegalArgumentException: argument type mismatch
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at flexjson.ObjectBinder.bindIntoObject(ObjectBinder.java:149)
    at flexjson.factories.ClassLocatorObjectFactory.instantiate(ClassLocatorObjectFactory.java:38)
    at flexjson.ObjectBinder.bind(ObjectBinder.java:95)
    at flexjson.ObjectBinder.bindIntoCollection(ObjectBinder.java:110)
    at flexjson.factories.ClassLocatorObjectFactory.instantiate(ClassLocatorObjectFactory.java:29)
    at flexjson.ObjectBinder.bind(ObjectBinder.java:95)
    at flexjson.ObjectBinder.bindIntoObject(ObjectBinder.java:149)
    at flexjson.factories.ClassLocatorObjectFactory.instantiate(ClassLocatorObjectFactory.java:38)
    at flexjson.ObjectBinder.bind(ObjectBinder.java:95)
    at flexjson.ObjectBinder.bind(ObjectBinder.java:74)
    at flexjson.JSONDeserializer.deserialize(JSONDeserializer.java:158)
    

    Any help to resolve this will be greatly appreciated.

     
  • You don't need the explicit mappings like that.  All you need is:

    JSONDeserializer<TrainingClass> deserializer = new JSONDeserializer<TrainingClass>().use(Date.class, transformer);
    TrainingClass training = deserializer.deserialize(json, TrainingClass.class);
    

    Somewhere along the way it's not converting something quite right and is passing a HashMap into one of these typed classes.  You might try commenting out some setters to see where it stops throwing that exception.  I'd start with the attendees field.  If you can get the exception to stop then you'll know which field has a mapping problem.

     
  • Jay
    Jay
    2012-12-17

    Without attendees list the 'argument type mismatch' exception goes away, but I'm still not sure about how to proceed with debugging the list.

     
  • Jay
    Jay
    2012-12-17

    Charlier, thanks for trying to help me.

    since attendees appears to be the problem I'm trying to deserialize it separately:

    { "attendees" : [
        {
          "status" : { "id" : 0, "code" : "Active" }, "updateDate" : "20121213", "testScore" : 0, "student" : { "id" : 1, "district" : { "id" : 1, "name" : "test county" }, "city" : "city", "name" : "new test", "address" : "123 main st", "stateCode" : "ST", "class" : { "id" : 11, "name" : "class 1" }, "zip" : "12345", "number" : "123456789", "dateOfBirth" : "19891213" }
        },
        {
          "status" : { "id" : 0, "code" : "Active" }, "updateDate" : "20121213", "testScore" : 0, "student" : { "id" : 2, "district" : { "id" : 2, "name" : "test county2" }, "city" : "city2", "name" : "new test2", "address" : "123 main st", "stateCode" : "ST", "class" : { "id" : 12, "name" : "class 2" }, "zip" : "12345", "number" : "223456789", "dateOfBirth" : "19881212" } 
        }
      ]
    }
    

    And to deserialize, I tried:

    JSONDeserializer<Map<String, List<ClassAttendee>>> deserializer = new JSONDeserializer<Map<String, List<ClassAttendee>>>()
     .use(Date.class, transformaer)
     .use("values",ArrayList.class)
     .use("values.values", ClassAttendee.class);
    List<ClassAttendee>  attendees = deserializer.deserialize(json,Map.class).get("attendees");
    

    But that doesn't do the trick (java.lang.IllegalArgumentException: argument type mismatch). Do you have any other suggestion?

     
  • Try this.  Change your Set to a List for attendees and see if that works.  If it does then there's something going on with Set deserialization.  If not it might be something inside attendees that's causing trouble.

     
  • Ok after reading your second post in doing the separation you pretty much did the List route.  You might have to post your ClassAttendee code for me to help any further because that's where the error is.

     
  • Jay
    Jay
    2012-12-17

    Here is the code,hope you can spot something :) Thanks.

    @Entity
    public class ClassAttendee implements Serializable {
        @NotNull
        @ManyToOne
        @JoinColumn(name = "student_id")
        private Student student;
        @NotNull
        @ManyToOne
        @JoinColumn(name = "trainingclass_id")
        private TrainingClass trainingClass;
        @NotNull
        @Column(name = "test_score")
        @Size(max = 3)
        private String testScore;
        @ManyToOne
        @JoinColumn(name = "status_id")
        private ClassAttendeeStatus status;
        
        @NotNull
        @Column(name = "creation_date")
        @Temporal(TemporalType.TIMESTAMP)
        @DateTimeFormat(style = "M-")
        private Date creationDate;
        @NotNull
        @Column(name = "update_date")
        @Temporal(TemporalType.TIMESTAMP)
        @DateTimeFormat(style = "M-")
        private Date updateDate;
       @Id
        @SequenceGenerator(name = "classAttendeeGen", sequenceName = "seq_attendees")
        @GeneratedValue(strategy = GenerationType.AUTO, generator = "classAttendeeGen")
        @Column(name = "id")
        private Long id;
        public Long getId() {
            return this.id;
        }
        public void setId(Long id) {
            this.id = id;
        }
        public Driver getStudent() {
            return this.student;
        }
        public void setStudent(Student student) {
            this.student = student;
        }
        public TrainingClass getTrainingClass() {
            return this.trainingClass;
        }
        public void setTrainingClass(TrainingClass trainingClass) {
            this.trainingClass = trainingClass;
        }
        public String getTestScore() {
            return this.testScore;
        }
        public void setTestScore(String testScore) {
            this.testScore = testScore;
        }
        public ClassAttendeeStatus getStatus() {
            return this.status;
        }
        public void setStatus(ClassAttendeeStatus status) {
            this.status = status;
        }   
        public Date getCreationDate() {
            return this.creationDate;
        }
        public void setCreationDate(Date creationDate) {
            this.creationDate = creationDate;
        }
        public Date getUpdateDate() {
            return this.updateDate;
        }
        public void setUpdateDate(Date updateDate) {
            this.updateDate = updateDate;
        }
    }
    
     
  • I think this is going to be your problem:

    "class" : { "id" : 11, "name" : "class 1" }
    

    You can't have a property called class because there is already a property on all Java objects called class already.  Every Java object supports a method getClass(), and so you can't define another method getClass() on it.  You might also want to look at testScore because the object has it as a String, but in the JSON it's a number.

     
  • Jay
    Jay
    2012-12-18

    Charlie,

    After fixing the data issues it is binding correctly! Should've figured out on my own :)

    Really appreciate your help! Thanks.