eclipse-ee4j / eclipse-ee4j/yasson
Error serializing HibernateProxy properties (member variables)
- Dominant language
- Java
- Stars
- 218
- Forks
- 109
- Avg merge
- 1d 5h
- Merged PRs (30d)
- 9
Description
Hi !
I'm trying to serialize an object that has hibernate proxies in it. Some of those proxies also contain other members that are proxies.
I can confirm that the LazyInitializer.getImplementation() returns a valid unproxied object in all instances.
Once i try to serialize the object, i get the following exception:
```
Caused by: javax.json.bind.JsonbException: Unable to serialize property 'pricePlanCurrent' from com.smithmicro.safepath.family.persistence.entity.Account
at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.ObjectSerializer.serializeInternal(ObjectSerializer.java:67)
at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.AbstractContainerSerializer.serialize(AbstractContainerSerializer.java:64)
at org.eclipse.yasson//org.eclipse.yasson.internal.Marshaller.serializeRoot(Marshaller.java:148)
at org.eclipse.yasson//org.eclipse.yasson.internal.Marshaller.marshall(Marshaller.java:76)
at org.eclipse.yasson//org.eclipse.yasson.internal.Marshaller.marshall(Marshaller.java:102)
at org.eclipse.yasson//org.eclipse.yasson.internal.JsonBinding.toJson(JsonBinding.java:118)
...
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.jboss.as.ee@18.0.1.Final//org.jboss.as.ee.component.ManagedReferenceMethodInterceptor.processInvocation(ManagedReferenceMethodInterceptor.java:52)
at org.jboss.invocation@1.5.2.Final//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at org.jboss.invocation@1.5.2.Final//org.jboss.invocation.InterceptorContext$Invocation.proceed(InterceptorContext.java:509)
at org.jboss.as.weld.common@18.0.1.Final//org.jboss.as.weld.interceptors.Jsr299BindingsInterceptor.delegateInterception(Jsr299BindingsInterceptor.java:79)
at org.jboss.as.weld.common@18.0.1.Final//org.jboss.as.weld.interceptors.Jsr299BindingsInterceptor.doMethodInterception(Jsr299BindingsInterceptor.java:89)
at org.jboss.as.weld.common@18.0.1.Final//org.jboss.as.weld.interceptors.Jsr299BindingsInterceptor.processInvocation(Jsr299BindingsInterceptor.java:102)
at org.jboss.as.ee@18.0.1.Final//org.jboss.as.ee.component.interceptors.UserInterceptorFactory$1.processInvocation(UserInterceptorFactory.java:63)
at org.jboss.invocation@1.5.2.Final//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at org.jboss.as.ejb3@18.0.1.Final//org.jboss.as.ejb3.component.invocationmetrics.ExecutionTimeInterceptor.processInvocation(ExecutionTimeInterceptor.java:43)
at org.jboss.invocation@1.5.2.Final//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at org.jboss.as.jpa@18.0.1.Final//org.jboss.as.jpa.interceptor.SBInvocationInterceptor.processInvocation(SBInvocationInterceptor.java:47)
at org.jboss.invocation@1.5.2.Final//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at org.jboss.as.ee@18.0.1.Final//org.jboss.as.ee.concurrent.ConcurrentContextInterceptor.processInvocation(ConcurrentContextInterceptor.java:45)
at org.jboss.invocation@1.5.2.Final//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at org.jboss.invocation@1.5.2.Final//org.jboss.invocation.InitialInterceptor.processInvocation(InitialInterceptor.java:40)
at org.jboss.invocation@1.5.2.Final//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at org.jboss.invocation@1.5.2.Final//org.jboss.invocation.ChainedInterceptor.processInvocation(ChainedInterceptor.java:53)
at org.jboss.as.ee@18.0.1.Final//org.jboss.as.ee.component.interceptors.ComponentDispatcherInterceptor.processInvocation(ComponentDispatcherInterceptor.java:52)
at org.jboss.invocation@1.5.2.Final//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at org.jboss.as.ejb3@18.0.1.Final//org.jboss.as.ejb3.component.pool.PooledInstanceInterceptor.processInvocation(PooledInstanceInterceptor.java:51)
at org.jboss.invocation@1.5.2.Final//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at org.jboss.as.ejb3@18.0.1.Final//org.jboss.as.ejb3.component.interceptors.AdditionalSetupInterceptor.processInvocation(AdditionalSetupInterceptor.java:54)
at org.jboss.invocation@1.5.2.Final//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at org.jboss.as.ejb3@18.0.1.Final//org.jboss.as.ejb3.tx.CMTTxInterceptor.invokeInNoTx(CMTTxInterceptor.java:216)
... 173 more
Caused by: javax.json.bind.JsonbException: Unable to serialize property 'hibernateLazyInitializer' from com.smithmicro.safepath.family.persistence.entity.PricePlan$HibernateProxy$Sne6Togw
at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.ObjectSerializer.serializeInternal(ObjectSerializer.java:67)
at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.AbstractContainerSerializer.serialize(AbstractContainerSerializer.java:64)
at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.AbstractContainerSerializer.serializerCaptor(AbstractContainerSerializer.java:96)
at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.ObjectSerializer.marshallProperty(ObjectSerializer.java:110)
at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.ObjectSerializer.serializeInternal(ObjectSerializer.java:65)
... 209 more
Caused by: javax.json.bind.JsonbException: Unable to serialize property 'readOnly' from org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor
at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.ObjectSerializer.serializeInternal(ObjectSerializer.java:67)
at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.AbstractContainerSerializer.serialize(AbstractContainerSerializer.java:64)
at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.AbstractContainerSerializer.serializerCaptor(AbstractContainerSerializer.java:96)
at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.ObjectSerializer.marshallProperty(ObjectSerializer.java:110)
at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.ObjectSerializer.serializeInternal(ObjectSerializer.java:65)
... 213 more
Caused by: javax.json.bind.JsonbException: Error getting value on: org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor@2a176e6e
at org.eclipse.yasson//org.eclipse.yasson.internal.model.GetValueCommand.getValue(GetValueCommand.java:36)
at org.eclipse.yasson//org.eclipse.yasson.internal.model.ReflectionPropagation.getValue(ReflectionPropagation.java:73)
at org.eclipse.yasson//org.eclipse.yasson.internal.model.PropertyModel.getValue(PropertyModel.java:284)
at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.ObjectSerializer.marshallProperty(ObjectSerializer.java:87)
at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.ObjectSerializer.serializeInternal(ObjectSerializer.java:65)
... 217 more
Caused by: java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.eclipse.yasson//org.eclipse.yasson.internal.model.GetFromGetter.internalGetValue(GetFromGetter.java:28)
at org.eclipse.yasson//org.eclipse.yasson.internal.model.GetValueCommand.getValue(GetValueCommand.java:34)
... 221 more
Caused by: org.hibernate.TransientObjectException: Proxy [com.smithmicro.safepath.family.persistence.entity.PricePlan#3] is detached (i.e, session is null). The read-only/modifiable setting is only accessible when the proxy is associated with an open session.
at org.hibernate@5.3.13.Final//org.hibernate.proxy.AbstractLazyInitializer.errorIfReadOnlySettingNotAvailable(AbstractLazyInitializer.java:343)
at org.hibernate@5.3.13.Final//org.hibernate.proxy.AbstractLazyInitializer.isReadOnly(AbstractLazyInitializer.java:356)
... 227 more
```
It seems that is trying to serialize the hibernate implementation of the LazyInitializer (ByteBuddy). I tried to use a Serializer to force Json-B to serialize the real object.
```
public class HibernateProxySerializer implements JsonbSerializer {
@Override
public void serialize(HibernateProxy object, JsonGenerator generator, SerializationContext serializer) {
serializer.serialize(object.getHibernateLazyInitializer().getImplementation(), generator);
}
}
```
The serializer seems to be used only when the root object is a proxy. If i pass any object that is not a proxy but contains one, the serializer is not used and the exception above is thrown. I suspect this is because the class of an HibernateProxy has inner classes and it is not the main class like $HibernateProxy$ hence:
```
private void marshallProperty(T object, JsonGenerator generator, SerializationContext ctx, PropertyModel propertyModel) {
(...)
final JsonbSerializer propertyCachedSerializer = propertyModel.getPropertySerializer();
```
Fails to find a UserSerializer for this.
The first workaround i found was to force the getter of a property (**GetFromGetter**) to unproxy the object. This is not optimal for the code and working around it involves using an oddly named function for the business logic "get" and the regular one for Json-B only . For code cleanup, it would be nice if we could annotate a property/method with @JsonbGetter to define the getter Json-B should use.
The second workaround was to create a visibility strategy that blocked fields or functions of type `LazyInitializer.class` and `ProxyConfiguration.class` . This is also not optimal but it seems to get the work done. I noticed that Yasson's default visibility strategy has the ability to look both at the field and method to judge the visibility of each, which is not possible in a custom visibility strategy, specially to enforce the spec.
I would like to have the `propertyModel.getPropertySerializer()` to return my `JsonbSerializer` for property serialization, not just the root object !
Thanks
-----------
Wildfly 18.0.1 Final
Yasson 1.0.5
JDK 11
MySQL Connector 8.0.15
Contributor guide
Research direction
Start in ObjectSerializer.marshallProperty and trace PropertyModel.getPropertySerializer(), comparing root serialization with nested property serialization. Reproduce the detached HibernateProxy case using the issue's Yasson 1.0.5 and JDK 11 setup; done means a registered HibernateProxy serializer is selected for nested proxies without serializing hibernateLazyInitializer or ByteBuddyInterceptor, with regression coverage.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- backend
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100