我遇到了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
}
}