Search code examples
hibernatejacksonjson-deserializationjsonserializer

MappingJackson2HttpMessageConverter Can not find a (Map) Key deserializer for type


Here are the entity classes for my project

import java.util.Iterator;
import java.util.Map;
import java.util.Set;

@Entity
@Table(name="training")
public class Training {

    @Id
    @GeneratedValue
    private long id;
    
    private String topic;
    
    
    @OneToMany(mappedBy="training")     
    private Set<Session> sessions = new HashSet<Session>();
    
    public Training(){
        
    }
    
    public Training(String topic, TransitionLevel level, Set<Session> sessions) {
        this.topic = topic;
        this.level = level;
        this.sessions = sessions;
    }


    public long getId() {
        return id;
    }


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


    public String getTopic() {
        return topic;
    }


    public void setTopic(String topic) {
        this.topic = topic;
    }


    public Set<Session> getSessions() {
        return sessions;
    }


    public void setSessions(Set<Session> sessions) {
        this.sessions = sessions;
    }
    
}

Here is the Session Table

    @Entity
    @Table(name="session")
    public class Session {
    
        @Id
        @GeneratedValue
        private long id;
        
        private String location;
        
        @ManyToOne  
        @JoinColumn(name="training_id") 
        @JsonIgnore
        private Training training;
        
        private Date start;
        
        private Date end;
        
        
        @JoinTable(name="session_user",
                joinColumns = @JoinColumn(name="session_id"),
                inverseJoinColumns = @JoinColumn(name="trainingRole_id"))
        @MapKeyJoinColumn(name="user_id")
        @ElementCollection  
        @JsonIgnore
        

    private Map&lt;User, TrainingRole&gt; users = new HashMap&lt;User, TrainingRole&gt;();</div>
        
        public long getId() {
            return id;
        }
    
    
        public void setId(long id) {
            this.id = id;
        }
    
    
        public String getLocation() {
            return location;
        }
    
    
        public void setLocation(String location) {
            this.location = location;
        }
    
    
        public Training getTraining() {
            return training;
        }
    
    
        public void setTraining(Training training) {
            this.training = training;
        }
    
    
        public Date getStart() {
            return start;
        }
    
    
        public void setStart(Date start) {
            this.start = start;
        }
    
    
        public Date getEnd() {
            return end;
        }
    
    
        public void setEnd(Date end) {
            this.end = end;
        }
    
    
        public Map &lt;User, TrainingRole&gt; getUsers() {
            return users;
        }
    
    
        public void setUsers(Map&lt;User, TrainingRole&gt; users) {
            this.users = users;
        }
            
    }

Here is the User Entity

@Entity
@Table(name="user")
public class User {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Column(name="id")
    private long id;
    
    @Column(name="csl",unique=true)
    private String csl;
    
    @Column(name="fullName")
    private String fullName;
    
    
    @Column(name="email")
    private String email;       
    
    public User() {

    }

    public long getId() {
        return id;
    }

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

    
    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }


    public String getCsl() {
        return csl;
    }


    public void setCsl(String csl) {
        this.csl = csl;
    }


    public String getFullName() {
        return fullName;
    }


    public void setFullName(String fullName) {
        this.fullName = fullName;
    }

}

I am using JPARepository to save training and session objects in my mysql database

But Whenever I am saving the training object or session object

I am getting the error

c.j.MappingJackson2HttpMessageConverter : Failed to evaluate Jackson deserialization for type [simple type, class Session]: com.fasterxml.jackson.databind.JsonMappingException: Can not find a (Map) Key deserializer for type [simple type, class User]

I googled it and found I need to serialize and deserialize manually, but I have no idea how to do that


Solution

  • To use your own classes as keys for maps you need a serializer and deserializer, like you indicate. Something like:

    public class CustomKeyDeserializer extends KeyDeserializer {
    
        private static ObjectMapper mapper = new ObjectMapper();
    
        @Override
        public Object deserializeKey(String key, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
            return mapper.readValue(key, User.class);
        }
    }
    
    public class CustomKeySerializer extends JsonSerializer<User> {
    
        private static ObjectMapper mapper = new ObjectMapper();
    
        @Override
        public void serialize(User value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
            gen.writeFieldName(mapper.writeValueAsString(value));
        }
    }
    

    And also annotate the field

        @JsonDeserialize(keyUsing = CustomKeyDeserializer.class)
        @JsonSerialize(keyUsing = CustomKeySerializer.class)
        private Map<User, TrainingRole> users = new HashMap<User, TrainingRole>();