1

Ich stoße auf ein Problem mit einer Spring Data REST-Anwendung mit dem Spring Boot-Starter. Ich habe eine Anzahl von Entitäten definiert mit verschiedenen Beziehungen. Ich kann sie mit dem Schlüssel abrufen (allgemein), aber ich habe Probleme mit einigen Sammlungen derselben Objekte. Ich frage mich, ob dies ein JsonIdentityInfo-Problem ist oder das einzigartige Bezeichner-Zeugs anderweitig "bricht", um eine zirkuläre JSON-Generierung zu verhindern.Spring Data REST - JsonGenerationException: Ich kann keine Zahl schreiben, erwarte Feldname

Es gibt ungefähr 500 Einträge in der Unternehmenstabelle und die Seitengröße ist standardmäßig auf 20 eingestellt. Wenn Sie zum Standardendpunkt (/ company) gehen, wird der folgende Fehler zurückgegeben. Ich kann einzelne Unternehmen ohne Problem (/ company/1) abrufen, einschließlich des Unternehmens, das beim Erstellen der JSON-Ausnahme generiert werden soll.

Wenn ich in den Ausnahme-Stack eintrete, sehe ich, dass es versucht, JSON für das Feld supportEmailAddress zu generieren. Dies ist eine Zeile, auf die mehrere Unternehmenszeilen verweisen können. Unternehmen haben auch Kontakte, die in dieser Tabelle auch E-Mail-Adressen haben, aber diese werden im Allgemeinen nicht zwischen Firmen oder Kontakten geteilt.

HINWEIS: Ich habe die SO-Frage mit einem ähnlichen Stack-Trace betrachtet, aber diese Frage schien sich um einen benutzerdefinierten Serializer zu drehen. Ich bin nicht mit einem benutzerdefinierten Serializer.

Ein paar Dinge, die ich versucht habe:

  • JsonIdentityInfo mit und ohne Rahmen Attribut
  • JsonManagedReference und JsonBackReference
  • @access (AccessType.PROPERTY) auf @Id Feld

Bibliotheksversionen:

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' 

Ich habe versucht, durch die Serialisierung zu debuggen und das Root-Problem ist, dass der Serializer bei der Verarbeitung der Firma.supportEmailAddress.key verwirrt wird. Es versucht, den Schlüsselwert auszugeben, aber der Serializer erwartet, dass der Schlüsselname der nächste ist. Der Fehler tritt auf, wenn der zweite Verweis auf dieselbe supportEmailAddress-ID erfolgt.

Aktualisiert auf Jackson 2.8.0. Keine Änderung.

Ich fügte ein vereinfachtes, handgeschriebenes Beispiel JSON am Ende hinzu, um die Struktur zu zeigen, die ich erwarte. Wie Sie sehen können, beziehen sich die beiden Firmen auf dasselbe supportEmailAddress-Objekt (gleiche ID). Wenn ich zu einer anderen ID übergehe, wird sie korrekt gerendert. Ich glaube, die zweite Referenz würde eigentlich nur die ID anstelle des Rests des Objekts ausgeben, da es bereits einmal serialisiert wurde. Es ist mein Verständnis, dass dies eine Standardfunktion ist, und ich habe nichts getan, um die Standardfunktionen von Jackson zu ändern.

Vereinfachte Entities (Accessoren weggelassen):

Firma:

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

Besitzer:

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

Kontakt:

012.351.
@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; 

Stapelüberwachung:

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 

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

Antwort

0

I wurde mit dem gleichen Fehler während mit EAGER auf den verschachtelten Einheiten angegeben fetch (z.B. @OneToMany(mappedBy = "owner", cascade = CascadeType.ALL) private Set<Contact> contacts; auf Ihrer Owner Einheit).

Sobald mehr als 1 Elemente in den verschachtelten Sammlungen vorhanden waren, funktionierte die Serialisierung nicht mehr. Ich konnte umgehen das Problem durch Angabe FetchType.LAZY auf den verschachtelten Sammlungen, denke ich, dass auf diese Weise der Serialisierer nicht mit der Rekursion verwechselt werden?

Ich würde gerne eine richtige Erklärung von jemand mehr mit Jackson

vertrauten hören hören
Verwandte Themen