JsonMappingException: Can not instantiate value of

2020-08-13 05:19发布

问题:

I have just added a REST api to my existing Spring + BlazeDS + Hibernate server and everything appears to work when data is retrieved and serialised as JSON but when I try and POST data to be de-serialised into a POJO I get the an exception.

I was under the impression that the spring annotations and the presence of the Jackson jars in the class path would be all that was required, at least it was for my list, get, delete methods that had simple parameters.

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

Here's the method being called:

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;
}
}

and here is the full exception:

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)

Update: added definition of Company 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 If I amend the DTO so that Jackson ignores the Company.adminUserIdList property then the record is created successfully.

@JsonIgnore
public Collection<Integer> getAdminUserIdList() { return this.adminUserIdList; }
@JsonIgnore
public void setAdminUserIdList (Collection<Integer> adminUserIdList) { this.adminUserIdList = adminUserIdList; }    

Update Here is the Json as returned by the /company/get/1 method using 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
}

I was using a similar pattern (less the "id" and a different "uid")for the /compamy/create call with a Content-type=application/json header

回答1:

I solved the same problem by fixing the JSON I was sending to the server; it was invalid. I removed the "," characters at the end of the last attributes and it worked. I hope it helps



回答2:

In my case, I was missing starting and ending curly braces.



回答3:

Similar to @Nirmal, I passed in JSON which was contained within double quotes:

"{ "SomeJSON":"Value" }"

instead of

{ "SomeJSON":"Value" }

Removing the "s resolved the issue.



回答4:

Also, I got this error message when I tried to instantiate an enum that wasn't properly defined in my swagger file. See How to define enum in swagger.io?