Je viens d'ajouter une api REST à mon serveur Spring + BlazeDS + Hibernate existant et tout semble fonctionner lorsque les données sont récupérées et sérialisées au format JSON mais lorsque j'essaie de désérialiser les données POST un POJO je reçois une exception.
J'avais l'impression que les annotations de printemps et la présence des pots de Jackson dans le chemin d'accès aux classes seraient tout ce qui était nécessaire, du moins c'était pour ma liste, get, delete, des méthodes ayant des paramètres simples.
org.codehaus.jackson.map.JsonMappingException: Can not instantiate value of type [simple type, class com.twoh.dto.Company] from JSON String; no single-String constructor/factory method
Voici la méthode appelée:
public abstract class BaseEntityService<T extends BaseEntity> implements IBaseEntityService<T> {
private IBaseEntityDAO<T> DAO;
@Autowired
private ValidationResultHelper validationResultHelper;
public void setDAO(IBaseEntityDAO<T> DAO) {
this.DAO = DAO;
}
...
@Secured("ROLE_USER")
@RequestMapping(value="/create", method=RequestMethod.POST)
public @ResponseBody ValidationResult create(@RequestBody T entity) {
ValidationResult result = null;
try {
result = DAO.persistEntity(entity);
} catch(JDBCException e) {
result = ExceptionHelper.getValidationResult(e);
} catch(DataIntegrityViolationException e) {
result = ExceptionHelper.getValidationResult(e);
}
validationResultHelper.log(DAO.getSession(), entity.getId(), entity.getClass(), result);
return result;
}
}
et voici l'exception complète:
org.codehaus.jackson.map.JsonMappingException: Can not instantiate value of type [simple type, class com.twoh.dto.Company] from JSON String; no single-String constructor/factory method
at org.codehaus.jackson.map.deser.std.StdValueInstantiator._createFromStringFallbacks(StdValueInstantiator.Java:379)
at org.codehaus.jackson.map.deser.std.StdValueInstantiator.createFromString(StdValueInstantiator.Java:268)
at org.codehaus.jackson.map.deser.BeanDeserializer.deserializeFromString(BeanDeserializer.Java:759)
at org.codehaus.jackson.map.deser.BeanDeserializer.deserialize(BeanDeserializer.Java:585)
at org.codehaus.jackson.map.ObjectMapper._readMapAndClose(ObjectMapper.Java:2723)
at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.Java:1914)
at org.springframework.http.converter.json.MappingJacksonHttpMessageConverter.readInternal(MappingJacksonHttpMessageConverter.Java:135)
at org.springframework.http.converter.AbstractHttpMessageConverter.read(AbstractHttpMessageConverter.Java:154)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.readWithMessageConverters(HandlerMethodInvoker.Java:633)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolveRequestBody(HandlerMethodInvoker.Java:597)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolveHandlerArguments(HandlerMethodInvoker.Java:346)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.Java:171)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.Java:436)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.Java:424)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.Java:790)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.Java:719)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.Java:669)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.Java:585)
at javax.servlet.http.HttpServlet.service(HttpServlet.Java:637)
at javax.servlet.http.HttpServlet.service(HttpServlet.Java:717)
at org.Apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.Java:290)
at org.Apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.Java:206)
at org.springframework.orm.hibernate3.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.Java:198)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.Java:76)
at org.Apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.Java:235)
at org.Apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.Java:206)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.Java:311)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.Java:116)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.Java:83)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.Java:323)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.Java:113)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.Java:323)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.Java:101)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.Java:323)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.Java:54)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.Java:323)
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.Java:45)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.Java:323)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.Java:182)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.Java:323)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.Java:105)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.Java:323)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.Java:87)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.Java:323)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.Java:173)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.Java:237)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.Java:167)
at org.Apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.Java:235)
at org.Apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.Java:206)
at org.Apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.Java:233)
at org.Apache.catalina.core.StandardContextValve.invoke(StandardContextValve.Java:191)
at org.Apache.catalina.core.StandardHostValve.invoke(StandardHostValve.Java:127)
at org.Apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.Java:102)
at org.Apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.Java:109)
at org.Apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.Java:298)
at org.Apache.coyote.http11.Http11Processor.process(Http11Processor.Java:857)
at org.Apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.Java:588)
at org.Apache.Tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.Java:489)
at Java.lang.Thread.run(Unknown Source)
Mise à jour: ajout de la définition de société DTO
@CheckDictionaryProperty.List({
@CheckDictionaryProperty(propertyName="partyId", dictionaryName="Party")
})
@Unique.List({
@Unique(properties = {"code"}, message = "UNIQUE_CODE"),
@Unique(properties = {"name"}, message = "UNIQUE_NAME")
})
@Entity
@FXClass
@Table(name="edrcompany")
@JsonAutoDetect
public class Company extends BaseEntity {
private static final long serialVersionUID = 1L;
public Company(){}
@NotBlank
@Column
private String name;
public String getName(){ return this.name; }
public void setName(String name){ this.name = name; }
@Column
private String code;
public String getCode() { return this.code; }
public void setCode(String code) { this.code = code; }
@NotNull
@Column(name="party_id")
private Integer partyId;
public Integer getPartyId() { return this.partyId; }
public void setPartyId(Integer partyId) { this.partyId = ValueHelper.isNullOrZero(partyId) ? null : partyId; }
@ElementCollection(targetClass=Integer.class, fetch=FetchType.EAGER)
@Fetch(FetchMode.SUBSELECT)
@CollectionTable(name="edrcompanyadminlink", joinColumns={@JoinColumn(name="company_id")})
@Column(name="user_id")
private Collection<Integer> adminUserIdList = new HashSet<Integer>();
public Collection<Integer> getAdminUserIdList() { return this.adminUserIdList; }
public void setAdminUserIdList (Collection<Integer> adminUserIdList) { this.adminUserIdList = adminUserIdList; }
}
@MappedSuperclass
@FXClass
public abstract class BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
public BaseEntity(){}
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Integer id;
public Integer getId() { return id; }
public void setId(Integer id) { this.id = ValueHelper.isNullOrZero(id) ? null : id; }
@Column(name="ENTITY_UID", unique=true, nullable=false, updatable=false, length=36)
/* Assign a default whenever this class is instantiated Hibernate will
* overwrite it when retrieving an entity from the DB.
*/
private String uid = UUID.randomUUID().toString();
public String getUID() { return uid; };
public void setUID(String uid) { this.uid = uid; }
@Version
@Column
private Integer version;
@FXIgnore
public Integer getVersion() { return this.version; }
public void setVersion(Integer version) { this.version = version; }
// Fake property so that DTO2FX will put it in
public String getClassName() { return this.getClass().getName(); }
@JsonIgnore
public void setClassName(String className) { throw new UnsupportedOperationException(); }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || !(o instanceof BaseEntity)) return false;
BaseEntity other = (BaseEntity) o;
// if the id is missing, return false
if (uid == null) return false;
// equivalence by uid
return uid.equals(other.getUID());
}
@Override
public int hashCode() {
if (uid != null) {
return uid.hashCode();
} else {
return super.hashCode();
}
}
@Override
public String toString() {
return this.getClassName() + ": " + this.getId();
}
}
Update Si je modifie le DTO de sorte que Jackson ignore la propriété Company.adminUserIdList, l’enregistrement est créé avec succès.
@JsonIgnore
public Collection<Integer> getAdminUserIdList() { return this.adminUserIdList; }
@JsonIgnore
public void setAdminUserIdList (Collection<Integer> adminUserIdList) { this.adminUserIdList = adminUserIdList; }
Update Voici le Json renvoyé par la méthode /company/get/1
à l'aide de FireFox RESTClient
{
"partyId":1,
"adminUserIdList":[21],
"name":"2H Mechanical LLC",
"code":null,
"uid":"fc5e15e7-a9a7-11e1-be90-7d08b05cbb96",
"id":1,
"className":"com.twoh.dto.Company",
"version":0
}
J'utilisais un modèle similaire (moins "id" et un "uid" différent) pour l'appel /compamy/create
avec un en-tête Content-type=application/json
J'ai résolu le même problème en corrigeant le JSON que j'envoyais au serveur. c'était invalide . J'ai enlevé les caractères "," à la fin des derniers attributs et cela a fonctionné . J'espère que ça aide
Dans mon cas, il me manquait le début et la fin des accolades.
Semblable à @Nirmal, j’ai passé en JSON, contenu entre guillemets:
"{
"SomeJSON":"Value"
}"
au lieu de
{
"SomeJSON":"Value"
}
Supprimer le "
s a résolu le problème.