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

Problem handling datatypes Recursive type parameters #76

Closed
aramk opened this issue Sep 22, 2012 · 18 comments
Closed

Problem handling datatypes Recursive type parameters #76

aramk opened this issue Sep 22, 2012 · 18 comments
Milestone

Comments

@aramk
Copy link

aramk commented Sep 22, 2012

I'm getting a stack overflow when trying to serialize this:

public class HashTree<K, V> extends HashMap<K, HashTree<K, V>>

Is this a limitation of Jackson or am I doing something wrong?

http://stackoverflow.com/questions/12466292/serializing-a-recursive-class-with-jackson-json

Thanks!

@cowtowncoder
Copy link
Member

Strange but true -- this does fail. I can simplify it for test.

I will also see that Java-classmate project (https://github.com/cowtowncoder/java-classmate) handles this cleanly; one of these days I should make Jackson use classmate's resolution as it's superior to Jackson's own.

@cowtowncoder
Copy link
Member

(note to self: yup, as usual, ClassMate resolves this without incident)

@cowtowncoder
Copy link
Member

Ok, so recursion here is not resolved because it is not via type variables (V extends Type), but sort of direct dependency. Need to see how to break that cycle with existing Jackson type resolution code.

@aramk
Copy link
Author

aramk commented Sep 22, 2012

ah ok, I'll check out java-classmate, cheers!

@cowtowncoder
Copy link
Member

One clarification: while java-classmate can handle this type, it unfortunately won't help too much with Jackson.

One thing here that might help is to use intermediate type in hierarchy, to try to untangle the reference.

I am still trying to figure out how to break the cycle: solution is not as easy to find as I hoped.

@aramk
Copy link
Author

aramk commented Sep 23, 2012

Ah ok I see. I've gotten around the issue by having an internal private
attribute handle the recursive data type - that seems to work.

On 23 September 2012 03:15, Tatu Saloranta notifications@github.com wrote:

One clarification: while java-classmate can handle this type, it
unfortunately won't help too much with Jackson.

One thing here that might help is to use intermediate type in hierarchy,
to try to untangle the reference.

I am still trying to figure out how to break the cycle: solution is not as
easy to find as I hoped.


Reply to this email directly or view it on GitHubhttps://github.com//issues/76#issuecomment-8790796.

@cowtowncoder
Copy link
Member

Ok good. I realized that TypeFactory obviously let's you build equivalent resolved type.
Bad thing about problem itself is that I was able to solve it only with changes that broke some existing tests, so it is possible that resolving this might take some time.

@aramk
Copy link
Author

aramk commented Sep 23, 2012

Ok, thanks again for getting back to me so quickly!

On 23 September 2012 14:05, Tatu Saloranta notifications@github.com wrote:

Ok good. I realized that TypeFactory obviously let's you build equivalent
resolved type.
Bad thing about problem itself is that I was able to solve it only with
changes that broke some existing tests, so it is possible that resolving
this might take some time.


Reply to this email directly or view it on GitHubhttps://github.com//issues/76#issuecomment-8795198.

@cowtowncoder cowtowncoder changed the title Recursive Data Types Problem handling datatypes Recursive type parameters Mar 17, 2014
@cowtowncoder
Copy link
Member

Looks like I might finally find a way to fix this -- when working on #728, noticed that one change fixed the failing test case here. Unfortunately it also broke 3 other tests, so need to dig bit deeper.

@cowtowncoder
Copy link
Member

Or not. Turns out that fix really is not correct, and while resolving fail on this case is not proper way to resolve it.

@dmill-bz
Copy link

Just wanted to say that I've been having this issue as well which is quite unfortunate as it requires a cyclic HashMap of generics and can't easily be rewritten.
You mentioned you would like to make jackson use java-classmate one of these days. Have you settled on a desired ETA or are you currently swamped with other projects/issues?

@cowtowncoder
Copy link
Member

@PommeVerte I have not settled on ETA. The problem is that it is quite difficult to have an idea of how much work is needed, and what the compatibility issues would be, from retrofitting JavaType to use classmate as backing resolver. So it has been moving on with minor releases as "hope to figure it out during next release".

@dmill-bz
Copy link

I see! Fingers crossed then. :)

@cowtowncoder cowtowncoder added this to the 2.7.0 milestone Oct 20, 2015
@cowtowncoder
Copy link
Member

Phew. With a major rewrite of the whole type handling system, this bug has been annihilated for 2.7.0.
There are some unrelated issues with type resolution to fix, but at the least this problem is no more.

@dmill-bz
Copy link

dmill-bz commented Nov 1, 2015

Great news!! Looking forward to 2.7.0. Are you guys aiming for a specific release date on this? I think there's an upcoming 2.6.4 in the works already so it would obviously be for after that?

@cowtowncoder
Copy link
Member

@PommeVerte No, we don't have a good idea wrt release date at this point, but if I had to guess, official release would be likely in early January, if release candidates were started in December 2015.
Patch releases are sometimes released concurrently; their releases do not typically have much effect on upcoming minor updates.

@voidmain
Copy link

voidmain commented Jul 16, 2016

I'm wondering if this bug still exists in 2.8.0. I upgraded to 2.8.0 and I'm still getting a StackOverflowError. Here's the class I'm binding:

public class DataDefinition extends HashMap<String, DataDefinition> {
  public DataDefinition definition;
  public DataDefinition elements;
  public String regex;
  public boolean required;
  public String type;
}

Here's a snippet of the stack trace I get:

    at com.fasterxml.jackson.databind.type.ResolvedRecursiveType.toString(ResolvedRecursiveType.java:90)
    at java.lang.String.valueOf(String.java:2994)
    at java.lang.StringBuilder.append(StringBuilder.java:131)
    at com.fasterxml.jackson.databind.type.MapType.toString(MapType.java:161)
    at java.lang.String.valueOf(String.java:2994)
    at java.lang.StringBuilder.append(StringBuilder.java:131)
    at com.fasterxml.jackson.databind.type.ResolvedRecursiveType.toString(ResolvedRecursiveType.java:90)
    at java.lang.String.valueOf(String.java:2994)

EDIT: I did some debugging and found that this looks like the location where the stack track starts:

    public StdValueInstantiator(DeserializationConfig config, JavaType valueType) {
        _valueTypeDesc = (valueType == null) ? "UNKNOWN TYPE" : valueType.toString();
        _valueClass = (valueType == null) ? Object.class : valueType.getRawClass();
    }

It's line 87 in StdValueInstantiator. That looks like it is calling toString() on the on the type and then it goes into an infinite recursion state. I'm not sure why the toString() call is necessary, but it seems like all of this is for debugging and errors and pre-calculating the string is preventing Jackson from working at all even if there isn't an error.

WORK-AROUND: In case anyone looks at this issue and needs a work around, you can use the @JsonAnySetter to fix this issue. Here's the code:

public class DataDefinition {
  public DataDefinition definition;
  public DataDefinition elements;
  @JsonAnySetter
  public Map<String, DataDefinition> properties = new HashMap<>();
  public String regex;
  public boolean required;
  public String type;
}

I also think that this can likely be fixed with code like this:

com.fasterxml.jackson.databind.type.MapType.java#toString()

    public String toString()
    {
        if ((_keyType instance ResolvedRecursiveType && ((ResolvedRecursiveType) _keyType).getSelfReferencedType() == this) || (_valueType instance ResolvedRecursiveType && ((ResolvedRecursiveType) _valueType).getSelfReferencedType() == this)) {
            return "[map type; class "+_class.getName()+", "+_keyType+" -> self]";
        }
        return "[map type; class "+_class.getName()+", "+_keyType+" -> "+_valueType+"]";
    }

@cowtowncoder
Copy link
Member

@voidmain could you please file a separate new issue with information you included -- this sounds like a new problem, possibly related to added caching for generic types.

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

4 participants