2016-07-11 96 views
1

我遇到了Spring Data REST應用程序使用Spring Boot啓動程序的問題。我有許多用各種關係定義的實體。我可以通過鍵(通常)檢索它們,但是我遇到了一些相同對象集合的問題。我想知道這是否是JsonIdentityInfo問題,或者是否「破壞」唯一標識符以防止循環JSON生成。Spring Data REST - JsonGenerationException:無法寫入數字,期望字段名稱

公司表中有~500個條目,頁面大小默認爲20.去默認端點(/公司),它返回下面的錯誤。我可以在沒有問題的情況下提取單個公司(/ company/1),包括與創建JSON異常時嘗試生成的公司相同的公司。

當我進入異常堆棧時,看到它正在嘗試爲supportEmailAddress字段生成JSON。這是可以被多個公司行引用的行。公司也在此表中具有EmailAddresses的聯繫人,但這些通常不在公司或聯繫人之間共享。

注意:我已經用類似的堆棧跟蹤看過SO問題,但是這個問題似乎圍繞着一個自定義的Serializer。我是而不是使用自定義序列化程序。

有幾件事情我已經試過:

  • JsonIdentityInfo使用和不使用scope屬性上@Id場
  • JsonManagedReference和JsonBackReference
  • @Access(AccessType.PROPERTY)

圖書館版本:

ext['hibernate.version'] = '5.1.0.Final' 
ext['hibernateVersion'] = '5.1.0.Final' 
ext['springVersion'] = '2.5.1.RELEASE' 
ext['springBootVersion'] = '1.3.5.RELEASE' 
ext['springDataCommonsVersion'] = '1.12.1.RELEASE' 
ext['springDataJpaVersion'] = '1.10.1.RELEASE' 
ext['springIntegrationVersion'] = '4.2.6.RELEASE' 
ext['querydslVersion'] = '4.1.0' 
ext['jacksonVersion'] = '2.8.0' 
ext['jacksonJsr310Version'] = '2.8.0' 

我試過通過序列化進行調試,根本問題是序列化程序在處理company.supportEmailAddress.key時會感到困惑。它試圖輸出鍵值,但序列化程序期望鍵名是下一個。當對同一個supportEmailAddress id進行第二次引用時會發生錯誤。

已更新至Jackson 2.8.0。不用找了。

我在最後添加了一個簡化的手寫示例JSON,以顯示我期待的結構。如您所見,這兩家公司引用相同的supportEmailAddress對象(相同的id)。如果我更改爲不同的ID,則呈現正確。我相信第二個引用實際上只是輸出id而不是對象的其餘部分,因爲它已經被序列化了一次。這是我的理解,這是一個標準功能,我沒有做任何與更改Jackson默認功能相關的任何內容。

簡化實體(存取忽略):

公司:

@Entity 
@Table(name = "T_Company") 
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "key", scope = Company.class) 
public class Company extends AbstractCustomEntity<Long> { 

@Id 
@GeneratedValue(strategy = GenerationType.SEQUENCE) 
@Column(name = "COMPANY_ID") 
private Long key; 

@Size(max = 50) 
@Column(name = "NAME", nullable = false, length = 50, unique = true) 
private String name; 

@OneToMany(orphanRemoval = true, cascade = CascadeType.ALL, mappedBy = "company") 
private Set<Alias> aliases; 

@ManyToMany(mappedBy = "company") 
private Set<Owner> owner; 

@OneToMany(mappedBy = "agency", cascade = CascadeType.ALL) 
private Set<Contact> contacts; 

@ManyToOne 
@JoinColumn(name = "SUPPORT_EMAIL_ADDRESS_ID") 
private EmailAddress supportEmailAddress; 

擁有者:

@Entity 
@Table(name = "T_OWNER") 
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "key", scope = Owner.class) 
public class Owner extends AbstractCustomEntity<Long> { 

@Id 
@GeneratedValue(strategy = GenerationType.SEQUENCE) 
@Column(name = "VENDOR_ID", nullable = false, updatable = false) 
private Long key; 

@NotNull 
@Size(max = 50) 
@Column(name = "NAME",nullable = false, length = 50, unique = true) 
private String name; 

@ManyToMany(cascade = CascadeType.ALL) 
@JoinTable(name = "T_OWNER_COMPANY" 
, joinColumns = {@JoinColumn(name = "OWNER_ID")} 
     , inverseJoinColumns = {@JoinColumn(name = "COMPANY_ID")} 
) 
private Set<Company> companies; 

@OneToMany(mappedBy = "owner", cascade = CascadeType.ALL) 
private Set<Contact> contacts; 

EmailAddress的:

@Entity 
@Table(name = "T_EMAIL_ADDR") 
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "key", scope = EmailAddress.class) 
public class EmailAddress extends AbstractCustomEntity<Long> { 

@Id 
@GeneratedValue(strategy = GenerationType.SEQUENCE) 
@Column(name = "EMAIL_ADDRESS_ID") 
private Long key; 

@Column(name = "EMAIL_ADDRESS_TYPE_NME") 
@Enumerated(EnumType.STRING) 
private EmailAddrType emailAddrType; 

@Size(max = 200) 
@Email 
@Column(name = "EMAIL_ADDR", length = 200, nullable = false, updatable = false) 
private String emailAddress; 

聯繫人:

@Entity 
@Table(name = "T_CONTACT") 
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) 
@DiscriminatorColumn(name = "CATG", length = 6) 
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "key", scope = Contact.class) 
public abstract class Contact extends AbstractCustomEntity<Long> { 

@Id 
@GeneratedValue(strategy = GenerationType.SEQUENCE) 
@Column(name = "CONTACT_ID") 
private Long key; 

@Column(name = "CONTACT_NME", length = 100) 
@Size(max = 100) 
private String name; 

@OneToOne 
@JoinColumn(name = "EMAIL_ADDRESS_ID") 
private EmailAddress emailAddress; 

堆棧跟蹤:

org.springframework.http.converter.HttpMessageNotWritableException: Could not write content: Can not write a number, expecting field name; nested exception is com.fasterxml.jackson.core.JsonGenerationException: Can not write a number, expecting field name 
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:276) 
at org.springframework.http.converter.AbstractGenericHttpMessageConverter.write(AbstractGenericHttpMessageConverter.java:100) 
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:222) 
at org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor.handleReturnValue(HttpEntityMethodProcessor.java:183) 
at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:81) 
at org.springframework.data.rest.webmvc.ResourceProcessorHandlerMethodReturnValueHandler.handleReturnValue(ResourceProcessorHandlerMethodReturnValueHandler.java:113) 
at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:81) 
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:126) 
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:832) 
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:743) 
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) 
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:961) 
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:895) 
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967) 
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:858) 
at javax.servlet.http.HttpServlet.service(HttpServlet.java:622) 
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843) 
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292) 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) 
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) 
at org.springframework.web.filter.AbstractRequestLoggingFilter.doFilterInternal(AbstractRequestLoggingFilter.java:220) 
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) 
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) 
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) 
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:87) 
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) 
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77) 
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) 
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:121) 
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) 
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) 
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) 
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212) 
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106) 
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502) 
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141) 
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) 
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88) 
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:522) 
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1095) 
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:672) 
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1502) 
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1458) 
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) 
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) 
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) 
at java.lang.Thread.run(Thread.java:745) 
Caused by: com.fasterxml.jackson.core.JsonGenerationException: Can not write a number, expecting field name 
at com.fasterxml.jackson.core.JsonGenerator._reportError(JsonGenerator.java:1676) 
at com.fasterxml.jackson.core.json.UTF8JsonGenerator._verifyValueWrite(UTF8JsonGenerator.java:925) 
at com.fasterxml.jackson.core.json.UTF8JsonGenerator.writeNumber(UTF8JsonGenerator.java:787) 
at com.fasterxml.jackson.databind.ser.std.NumberSerializers$LongSerializer.serialize(NumberSerializers.java:188) 
at com.fasterxml.jackson.databind.ser.impl.WritableObjectId.writeAsId(WritableObjectId.java:35) 
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase._serializeWithObjectId(BeanSerializerBase.java:584) 
at com.fasterxml.jackson.databind.ser.impl.UnwrappingBeanSerializer.serialize(UnwrappingBeanSerializer.java:114) 
at com.fasterxml.jackson.databind.ser.impl.UnwrappingBeanPropertyWriter.serializeAsField(UnwrappingBeanPropertyWriter.java:127) 
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:678) 
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:157) 
at com.fasterxml.jackson.databind.SerializerProvider.defaultSerializeValue(SerializerProvider.java:985) 
at org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module$PersistentEntityResourceSerializer.serialize(PersistentEntityJackson2Module.java:193) 
at org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module$PersistentEntityResourceSerializer.serialize(PersistentEntityJackson2Module.java:140) 
at com.fasterxml.jackson.databind.SerializerProvider.defaultSerializeValue(SerializerProvider.java:985) 
at org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module$NestedEntitySerializer.serialize(PersistentEntityJackson2Module.java:356) 
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:672) 
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:678) 
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase._serializeWithObjectId(BeanSerializerBase.java:600) 
at com.fasterxml.jackson.databind.ser.impl.UnwrappingBeanSerializer.serialize(UnwrappingBeanSerializer.java:114) 
at com.fasterxml.jackson.databind.ser.impl.UnwrappingBeanPropertyWriter.serializeAsField(UnwrappingBeanPropertyWriter.java:127) 
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:678) 
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:157) 
at com.fasterxml.jackson.databind.SerializerProvider.defaultSerializeValue(SerializerProvider.java:985) 
at org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module$PersistentEntityResourceSerializer.serialize(PersistentEntityJackson2Module.java:193) 
at org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module$PersistentEntityResourceSerializer.serialize(PersistentEntityJackson2Module.java:140) 
at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serializeContents(IndexedListSerializer.java:119) 
at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:79) 
at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:18) 
at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFields(MapSerializer.java:616) 
at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:519) 
at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:31) 
at org.springframework.hateoas.hal.Jackson2HalModule$HalResourcesSerializer.serialize(Jackson2HalModule.java:340) 
at org.springframework.hateoas.hal.Jackson2HalModule$HalResourcesSerializer.serialize(Jackson2HalModule.java:302) 
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:672) 
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:678) 
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:157) 
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:130) 
at com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1428) 
at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:930) 
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:269) 
... 57 more 

例JSON:

{ 
    "_embedded" : { 
"companies" : [ { 
    "key" : 1, 
    "name" : "company1", 
    "supportEmailAddress" : { 
    "key" : 1, 
    "emailAddrType" : "support", 
    "emailAddress" : "[email protected]" 
    } 
    "aliases" : [ ], 
    "contacts" : [ ], 
    "owner" : { 
    "key" : 1, 
    "name" : "owner 1", 
    "contacts" : [ ], 
    "_links" : { 
     "company" : { 
     "href" : "http://localhost:8080/company/1" 
     } 
    } 
    }, 
    "_links" : { 
    "self" : { 
     "href" : "http://localhost:8080/company/1" 
    }, 
    "company" : { 
     "href" : "http://localhost:8080/company/1" 
    }, 
    "aliases" : { 
     "href" : "http://localhost:8080/company/1/aliases" 
    }, 
    "contacts" : { 
     "href" : "http://localhost:8080/company/1/contacts" 
    } 
    } 
}, { 
    "key" : 2, 
    "name" : "company2", 
    "supportEmailAddress" : { 
    "key" : 1, 
    "emailAddrType" : "support", 
    "emailAddress" : "[email protected]" 
    } 
    "aliases" : [ ], 
    "contacts" : [ ], 
    "owner" : { 
    "key" : 2, 
    "name" : "owner 2", 
    "contacts" : [ ], 
    "_links" : { 
     "company" : { 
     "href" : "http://localhost:8080/company/2" 
     } 
    } 
    }, 
    "_links" : { 
    "self" : { 
     "href" : "http://localhost:8080/company/2" 
    }, 
    "company" : { 
     "href" : "http://localhost:8080/company/2" 
    }, 
    "aliases" : { 
     "href" : "http://localhost:8080/company/2/aliases" 
    }, 
    "contacts" : { 
     "href" : "http://localhost:8080/company/2/contacts" 
    } 
    } 
} ] 
    }, 
    "_links" : { 
"self" : { 
    "href" : "http://localhost:8080/company" 
}, 
"profile" : { 
    "href" : "http://localhost:8080/profile/company" 
}, 
"search" : { 
    "href" : "http://localhost:8080/company/search" 
} 
    }, 
    "page" : { 
"size" : 20, 
"totalElements" : 2, 
"totalPages" : 1, 
"number" : 0 
    } 
} 

回答

0

我正在同時具有預先抓取該嵌套實體指定的相同的錯誤(例如@OneToMany(mappedBy = "owner", cascade = CascadeType.ALL) private Set<Contact> contacts;在您的Owner實體)。

只要嵌套集合中有多個項目,序列化就停止工作。我能工作圍繞問題通過指定FetchType.LAZY對嵌套的集合,我想這種方式串行器不會與遞歸混淆?

我很想聽聽更熟悉傑克遜的人的正確解釋

相關問題