1

Je rencontre un problème avec une application Spring Data REST utilisant le démarreur Spring Boot. J'ai un certain nombre d'entités définies avec diverses relations. Je suis capable de les récupérer par clé (généralement), mais j'ai des problèmes avec certaines collections des mêmes objets. Je me demande si c'est un problème de JsonIdentityInfo ou autrement "casser" le truc d'identifiant unique pour empêcher la génération circulaire de JSON.Spring Data REST - JsonGenerationException: Impossible d'écrire un nombre, en attendant le nom du champ

La table Company contient environ 500 entrées et la taille de la page est définie par défaut sur 20. Si vous passez au point de terminaison par défaut (/ company), l'erreur ci-dessous s'affiche. Je peux extraire les entreprises individuelles sans problème (/ company/1), y compris la même société qu'elle essaie de générer lorsque l'exception JSON est créée.

Lorsque j'entre dans la pile d'exceptions, je vois qu'il essaie de générer du JSON pour le champ supportEmailAddress. Ceci est une ligne qui peut être référencée par plusieurs lignes de la société. Les entreprises ont également des contacts qui ont EmailAddresses dans cette table, mais ceux-ci ne sont généralement pas partagés entre les entreprises ou les contacts.

REMARQUE: J'ai examiné la question SO avec une trace de pile similaire, mais cette question semblait s'articuler autour d'un sérialiseur personnalisé. Je suis pas en utilisant un sérialiseur personnalisé.

Quelques choses que j'ai essayé:

  • JsonIdentityInfo avec et sans attribut scope
  • JsonManagedReference et JsonBackReference
  • @access (AccessType.PROPERTY) sur le champ @Id

Versions de la bibliothèque:

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' 

J'ai essayé le débogage à travers la sérialisation et le problème de base est que le sérialiseur est confus lors du traitement de company.supportEmailAddress.key. Il tente d'afficher la valeur de la clé, mais le sérialiseur s'attend à ce que le nom de la clé soit le suivant. L'erreur se produit lorsque la deuxième référence au même ID supportEmailAddress est effectuée.

Mis à jour pour Jackson 2.8.0. Pas de changement.

J'ai ajouté un exemple simplifié, manuscrit JSON à la fin pour montrer la structure que j'attends. Comme vous pouvez le voir, les deux sociétés se réfèrent au même objet supportEmailAddress (même ID). Si je change à un id différent, il rend correctement. Je crois que la deuxième référence sortirait simplement l'identifiant au lieu du reste de l'objet, puisqu'il était déjà sérialisé une fois. Je crois comprendre que c'est une fonctionnalité standard, et je n'ai rien fait pour changer les caractéristiques par défaut de Jackson.

Entités simplifiées (de accesseurs omis):

Société:

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

Propriétaire:

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

Contact:

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

Trace de la pile:

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 

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

Répondre

0

je recevais la même erreur tout en ayant chargement aggressif spécifié sur les entités imbriquées (par exemple, @OneToMany(mappedBy = "owner", cascade = CascadeType.ALL) private Set<Contact> contacts; sur votre entité Owner).

Dès qu'il y avait plus de 1 éléments dans les collections imbriquées, la sérialisation a cessé de fonctionner. J'ai été en mesure de contourner le problème en spécifiant FetchType.LAZY sur les collections imbriquées, je suppose que de cette façon le sérialiseur ne se confond pas avec la récursivité?

J'aimerais entendre une bonne explication de quelqu'un de plus familier avec Jackson