Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot round-trip If DefaultTyping.NON_FINAL is used #15

Closed
fsparv opened this issue Apr 30, 2017 · 4 comments
Closed

Cannot round-trip If DefaultTyping.NON_FINAL is used #15

fsparv opened this issue Apr 30, 2017 · 4 comments

Comments

@fsparv
Copy link

fsparv commented Apr 30, 2017

Issue #5 explores serializing with JSOG without requiring annotations on the objects. A fabulous mixin-type workaround/solution was found. This is great for code cleanliness, but EVEN BETTER, it leads to the ability to JSOG serialize and deserialize 3rd party objects for which we have no hope of adding annotations. JSOG is absolutely required in such a case because 3rd party classes may well have circular references that lead to StackOverflowError.

The workaround however doesn't quite work in the generalized case due to a bug.

To handle any arbitrary class that a 3rd party might throw at you, you have to handle polymorphism. Jackson documentation shows 4 levels of default typing and all 4 levels cooperate well on serialization, but when the resulting JSON is deserialized here's what I've found:

  • DefaultTyping.JAVA_LANG_OBJECT - always works in jackson 2.5.0+ (required by JSOG)
  • DefaultTyping.OBJECT_AND_NON_CONCRETE - works in Jackson 2.5.4+
  • DefaultTyping.NON_CONCRETE_AND_ARRAYS - works in Jackson 2.5.4+
  • DefaultTyping.NON_FINAL - fails in all tested Jackson versions (I tested 2.5.0,2.5.1,2.5.3,2.5.4,2.5.5,2.6.0,2.8.8 when writing this issue)

I will be adding a pull request shortly with test demonstrating the problem.

This issue is based on the basic thesis that

    Thing t = new Thing
    String json = mapper.writeValueAsString(t);
    mapper.readValue(json, Thing.class);

should always succeed regardless of the settings, and with or without jsog. I am filing this here since It's demonstrated with JSOG, and removing JSOG mixin for Object.class prevents the exception on deserialize (but of course goes back to failing on serializing circular references, and duplicating objects in DAG's).

Note also that the structure doesn't have to actually BE polymorphic to set this off. I believe the issue is that jsog-jackson tries to interpose JSOGRef for objects, but there's no wrapper array with typing information for the JSOGRef, and so Jackson fails out, looking for the array that should pair the type designator, with the actual value.

Here's the associated stack trace for reference...

com.fasterxml.jackson.databind.JsonMappingException: Unexpected token (VALUE_STRING), expected START_ARRAY: need JSON Array to contain As.WRAPPER_ARRAY type information for class com.voodoodyne.jackson.jsog.JSOGRef
 at [Source: [ "com.voodoodyne.jackson.jsog.Thing", {
  "@id" : "1",
  "name" : "foo",
  "date" : [ "java.util.Date", 1493567469231 ]
} ]; line: 2, column: 3] (through reference chain: com.voodoodyne.jackson.jsog.Thing["@id"])

	at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148)
	at com.fasterxml.jackson.databind.DeserializationContext.wrongTokenException(DeserializationContext.java:927)
	at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._locateTypeId(AsArrayTypeDeserializer.java:127)
	at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._deserialize(AsArrayTypeDeserializer.java:93)
	at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer.deserializeTypedFromAny(AsArrayTypeDeserializer.java:68)
	at com.fasterxml.jackson.databind.JsonDeserializer.deserializeWithType(JsonDeserializer.java:151)
	at com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:42)
	at com.fasterxml.jackson.databind.deser.impl.ObjectIdValueProperty.deserializeSetAndReturn(ObjectIdValueProperty.java:85)
	at com.fasterxml.jackson.databind.deser.impl.ObjectIdValueProperty.deserializeAndSet(ObjectIdValueProperty.java:77)
	at com.fasterxml.jackson.databind.deser.impl.BeanPropertyMap._findDeserializeAndSet2(BeanPropertyMap.java:313)
	at com.fasterxml.jackson.databind.deser.impl.BeanPropertyMap.findDeserializeAndSet(BeanPropertyMap.java:291)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:335)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeWithObjectId(BeanDeserializerBase.java:1045)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:140)
	at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._deserialize(AsArrayTypeDeserializer.java:110)
	at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer.deserializeTypedFromObject(AsArrayTypeDeserializer.java:58)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeWithType(BeanDeserializerBase.java:966)
	at com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:42)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3564)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2580)
	at com.voodoodyne.jackson.jsog.Issue5WorkaroundTest.testWithPolymorphicStrategy(Issue5WorkaroundTest.java:55)
	at com.voodoodyne.jackson.jsog.Issue5WorkaroundTest.testIssue5WorkaroundNonFinal(Issue5WorkaroundTest.java:36)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:84)
	at org.testng.internal.Invoker.invokeMethod(Invoker.java:714)
<SNIP> test ng and above
fsparv pushed a commit to fsparv/jsog-jackson that referenced this issue Apr 30, 2017
@fsparv
Copy link
Author

fsparv commented Apr 30, 2017

At Jackson 2.7 the exception changes subtly, apparently getting further before experiencing an error (line 2 col 11 instead of line 2 col 3).

com.fasterxml.jackson.databind.JsonMappingException: Unexpected token (VALUE_STRING), expected START_ARRAY: need JSON Array to contain As.WRAPPER_ARRAY type information for class com.voodoodyne.jackson.jsog.JSOGRef
 at [Source: [ "com.voodoodyne.jackson.jsog.Thing", {
  "@id" : "1",
  "name" : "foo",
  "date" : [ "java.util.Date", 1493572420790 ]
} ]; line: 2, column: 11] (through reference chain: com.voodoodyne.jackson.jsog.Thing["@id"])

	at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:216)
	at com.fasterxml.jackson.databind.DeserializationContext.wrongTokenException(DeserializationContext.java:962)
	at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._locateTypeId(AsArrayTypeDeserializer.java:127)
	at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._deserialize(AsArrayTypeDeserializer.java:93)
	at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer.deserializeTypedFromAny(AsArrayTypeDeserializer.java:68)
	at com.fasterxml.jackson.databind.JsonDeserializer.deserializeWithType(JsonDeserializer.java:149)
	at com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:42)
	at com.fasterxml.jackson.databind.deser.impl.ObjectIdValueProperty.deserializeSetAndReturn(ObjectIdValueProperty.java:79)
	at com.fasterxml.jackson.databind.deser.impl.ObjectIdValueProperty.deserializeAndSet(ObjectIdValueProperty.java:71)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:341)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeWithObjectId(BeanDeserializerBase.java:1094)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:131)
	at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._deserialize(AsArrayTypeDeserializer.java:110)
	at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer.deserializeTypedFromObject(AsArrayTypeDeserializer.java:58)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeWithType(BeanDeserializerBase.java:1017)
	at com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:42)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3788)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2779)
	at com.voodoodyne.jackson.jsog.Issue15Test.testWithPolymorphicStrategy(Issue15Test.java:58)
	at com.voodoodyne.jackson.jsog.Issue15Test.testIssue5WorkaroundNonFinal(Issue15Test.java:35)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:84)
	at org.testng.internal.Invoker.invokeMethod(Invoker.java:714)
<SNIP> testng and above

fsparv pushed a commit to fsparv/jsog-jackson that referenced this issue Apr 30, 2017
@fsparv
Copy link
Author

fsparv commented Apr 30, 2017

Added a proposed fix to the pull request. Essentially upgrade to 2.5.4 and make JSOGRef a final class

@nsoft
Copy link
Contributor

nsoft commented May 19, 2017

ping?

stickfigure added a commit that referenced this issue Nov 5, 2018
@stickfigure
Copy link
Member

Huh weird, I don't recall getting any notifications of these msgs - sorry.

Merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants