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

JSOG support without the need to annotate the classes #5

Open
donshop11 opened this issue Jun 28, 2015 · 12 comments
Open

JSOG support without the need to annotate the classes #5

donshop11 opened this issue Jun 28, 2015 · 12 comments

Comments

@donshop11
Copy link

is it possible to make JSOG work without annotating the class hierarchy?

I have a class hierarchy which I do not control and need to serialize into JSON, unfortunately it has cyclic references. I understand it requires some book keeping (checking for each object if it was already referenced) , but sometimes it can be the only way to use JSOG.

@stickfigure
Copy link
Member

This is really a question for Tatu on the Jackson mailing list. FWIW, I'd like to know the answer myself. I'll ask and report back.

@stickfigure
Copy link
Member

Tatu says:

Yes, as with all Annotation-backed configuration, implementing AnnotationIntrospector (either sub-classing JacksonAnnotationIntrospector or NopAnnotationIntrospector), registering it with ObjectMapper allows use of alternative configuration sources.

There is no in-built equivalent of "default typing", although theoretically similar system could be added for object identity handling as well, if that seems like a good idea.

I'm turning this into a feature request to add an AnnotationIntrospector that enables this behavior.

@edobry
Copy link

edobry commented Jun 30, 2016

👍 , would love to see this implemented, or alternatively some direction on how I could do it myself

@stickfigure
Copy link
Member

Unfortunately my Jackson-fu is inadequate, but we'll keep this open until somebody figures it out.

@cjalmeida
Copy link

I didn't test it but you could create a "Mixin" class with the JsonIdentityInfo annotation. See:

http://wiki.fasterxml.com/JacksonMixInAnnotations

You can even set the mixin to be applied to all classes:

objectMapper.getSerializationConfig().addMixInAnnotations(Object.class, JSOGMixin.class);
objectMapper.getDeserializationConfig().addMixInAnnotations(Object.class, JSOGMixin.class);

(BTW the ObjectMapper was meant to be created once and shared across the application)

@cjalmeida
Copy link

BTW, I did test and works as charm!

Create a simple class as mixin:

@JsonIdentityInfo(generator = JSOGGenerator.class)
public static class JSOGMixin {

}

Then create a SimpleModule and append to your ObjectMapper

SimpleModule jModule = new SimpleModule();
jModule.setMixInAnnotation(Object.class, JSOGMixin.class);

@fsparv
Copy link

fsparv commented Apr 28, 2017

I'm having some difficulty round tripping using the SimpleModule approach... when I try

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

I get

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.foo.model.Thing", {
  "@id" : "1",
  "name" : "foo",
  "date" : [ "java.util.Date", 1493408755084 ]
} ]; line: 2, column: 11] (through reference chain: com.foo.model.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.fasterxml.jackson.databind.ObjectMapper$readValue$2.call(Unknown Source)
	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
	
<SNIP intellij groovy console classes>

Same result for

mapper.addMixIn(Object.class, JSOGMixin.class);

Class is like this:

package com.foo.model;
import java.util.Date;

public class Thing {
  public String name;
  public Date date;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public Date getDate() {
    return date;
  }

  public void setDate(Date date) {
    this.date = date;
  }
}

This was with Jackson 2.5.1 and 2.7.0. I checked out the code from this project and wrote this in unit test form and got a different exception due to 2.5.0 dep in your pom:

java.lang.IllegalArgumentException: Invalid type id 'com.voodoodyne.jackson.jsog.Thing' (for id type 'Id.class'): Class com.voodoodyne.jackson.jsog.Thing is not assignable to com.voodoodyne.jackson.jsog.JSOGRef

	at com.fasterxml.jackson.databind.jsontype.impl.ClassNameIdResolver._typeFromId(ClassNameIdResolver.java:68)
	at com.fasterxml.jackson.databind.jsontype.impl.ClassNameIdResolver.typeFromId(ClassNameIdResolver.java:48)
	at com.fasterxml.jackson.databind.jsontype.impl.TypeDeserializerBase._findDeserializer(TypeDeserializerBase.java:157)
	at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._deserialize(AsArrayTypeDeserializer.java:94)
	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.ObjectIdReader.readObjectReference(ObjectIdReader.java:136)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectId(BeanDeserializerBase.java:1045)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeWithType(BeanDeserializerBase.java:953)
	at com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:42)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3560)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2576)
	at com.voodoodyne.jackson.jsog.issue5WorkaroundTest.testIssue5Workaround(issue5WorkaroundTest.java:29)
	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.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)

<SNIP junit classes>

Caused by: java.lang.IllegalArgumentException: Class com.voodoodyne.jackson.jsog.Thing is not assignable to com.voodoodyne.jackson.jsog.JSOGRef
	at com.fasterxml.jackson.databind.JavaType._assertSubclass(JavaType.java:439)
	at com.fasterxml.jackson.databind.JavaType.narrowBy(JavaType.java:149)
	at com.fasterxml.jackson.databind.type.TypeFactory.constructSpecializedType(TypeFactory.java:221)
	at com.fasterxml.jackson.databind.jsontype.impl.ClassNameIdResolver._typeFromId(ClassNameIdResolver.java:64)
	... 40 more

@fsparv
Copy link

fsparv commented Apr 28, 2017

Ah ha.. my simple case above succeeds if I remove

mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL)

which I had previously added to handle abstract types (in more complicated code for actual use), so it seems that the workaround by @cjalmeida only stumbles if one is trying to use this technique to handle abstract types

@chaoyangnz
Copy link

Please prioritize this. For some classes in 3rd party library, we cannot add annotations, we just need to serialize to JSON.

Thanks

@stickfigure
Copy link
Member

@chaoyangnz what's wrong with using jackson mixins? That's the natural jackson solution to "I can't annotate my classes".

@chaoyangnz
Copy link

@stickfigure

I have a Integer field in my pojo, I applied the mixin, and I got an exception:

java.lang.Integer cannot be cast to com.voodoodyne.jackson.jsog.JSOGRef

I will isolate the issue and provide you the example shortly.

@chaoyangnz
Copy link

@stickfigure

found the cause. the mixin is applied to Object.class, but we have some classes using @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")

which made the conflicts.

Do you know how i can set mixin in all classes except some classes?

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

No branches or pull requests

6 participants