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

Failed to specialize Map type during serialization where type key type incompatibility overidden via "raw" types #1964

Closed
ptirador opened this issue Mar 10, 2018 · 16 comments
Milestone

Comments

@ptirador
Copy link

ptirador commented Mar 10, 2018

I'm using a class with a Map attribute, whose value is a Collection. I'm using OrientDB, so when I obtain this object from database, it's returning a OTrackedMap object.

When I'm trying to serialize it, it works perfect until Jackson v2.9.2. If I upgrade it to v2.9.3 or v2.9.4, it crashes with the following exception:

INFO: Jackson version: 2.9.4

com.fasterxml.jackson.databind.JsonMappingException: Failed to specialize base type java.util.Map<java.lang.String,java.util.Collection<java.lang.String>> as com.orientechnologies.orient.core.db.record.OTrackedMap, problem: Type parameter #1/2 differs; can not specialize java.lang.String with java.lang.Object (through reference chain: com.example.jackson.AccessModel["repositoryPrivileges"])

	at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:391)
	at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:351)
	at com.fasterxml.jackson.databind.ser.std.StdSerializer.wrapAndThrow(StdSerializer.java:316)
	at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:727)
	at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155)
	at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480)
	at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:319)
	at com.fasterxml.jackson.databind.ObjectMapper._configAndWriteValue(ObjectMapper.java:3893)
	at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:3207)
	at com.example.jackson.JacksonTest.testSerialization(JacksonTest.java:37)
	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)
	[...]

You can take a look at this gist to see an example.

@cowtowncoder
Copy link
Member

Looks like there is some kind of a problem with type compatibility of declarations.

What would be most helpful would be a simplified example, wherein OTrackedMap would be simplified into equivalent minimal declaration. The problem has to do with Map key type declaration (Object vs String), but I am not 100% sure whether flaw is in Jackson's checks of compatibility, or problem with declarations that try to downcast type.
So to solve that part it would be good to have even more compact example (since ideally we wouldn't add new external dependencies even for tests; and since avoiding that is doable here).

@ptirador
Copy link
Author

ptirador commented Mar 12, 2018

I've updated the gist with a working example.
Like you said, the problem had to do with Map key type declaration. If the key is changed from String to Object, it works. I guess that it's related to the Map implementation, which extends from LinkedHashMap<Object, T>.

@antonio-tomac
Copy link

I didn't want to create new issue, but I'm also having same exception when serializing or deserializing classes with nested generic types.
Here is a simple project with reduced example model and test to verify presence of problem.
Difference is that it affects versions 2.8.11 up to current latest 2.9.4, but works OK for versions <=2.8.10

@cowtowncoder
Copy link
Member

@ptirador Then it may be problem with declarations: as I understand it, it is not legal to subtype with different type parameters, except for return type co-variance (of type itself, but not its parameterization?).

@antonio-tomac Same exception does not necessarily mean same root cause, but I guess it's fine to be tacked here for now.

@carlspring
Copy link

Any update on this?

@steve-todorov
Copy link

@cowtowncoder are there any news/updates regarding this issue? :)

@cowtowncoder
Copy link
Member

If there is an update I will add an update. So no.

@julseik
Copy link

julseik commented Mar 28, 2018

As far as I can see, the problem seems to be in TypeFactory#_verifyAndResolvePlaceholders in line 460. There is checked, if the two raw classes are equal. In this example this would be Object and String. In my opinion instanceof or isAssignableFrom should be used instead.

@cowtowncoder
Copy link
Member

@julseik I can have a look to see if that makes sense. Type assignability can be bit dangerous, so I want to make sure this is legal.

@cowtowncoder cowtowncoder added the need-test-case To work on issue, a reproduction (ideally unit test) needed label Apr 5, 2018
@cowtowncoder
Copy link
Member

@ptirador I am bit confused. With 2.9.5, gist given works. How does the failing case differ from this?

@ptirador
Copy link
Author

ptirador commented Apr 5, 2018

@cowtowncoder Sorry, I've just updated the gist. Could you try now with that one? I've tried with versions from 2.9.3 to 2.9.5 and it does not work.

@dukejansen
Copy link

I'm also seeing this problem with 2.9.3, 2.9.4, and 2.9.5. I'll be happy to test any RC with a fix.

@cowtowncoder cowtowncoder removed the need-test-case To work on issue, a reproduction (ideally unit test) needed label Apr 19, 2018
@cowtowncoder
Copy link
Member

Ok. I don't think original test is valid, and to me it looks like Jackson might be doing the right thing here.
Note that declaration:

static class CustomMap<T> extends LinkedHashMap<Object, T> { }

forces key type to be Object. That is not String, and you can not derive CustomType in any way to make its key type be String.

Test works around this by using "raw" type:

    Map<String, Collection<String>> repoPrivilegesMap = new CustomMap();

since compiler would correctly catch the issue if trying to use

    Map<String, Collection<String>> repoPrivilegesMap = new CustomMap<>();

(where type assingments do not work)

So. Type system used here is inconsistent.

Having said that, this is for serialization side and given that it is possible to force type mismatch, perhaps there is a case to be made that some kinds of coercion should be possible.
If possible to do safely I would be ok for example if key type of Maps would be allowed to go from any other type down to Object, given that it is unlikely to hurt in unrelated cases, and would solve the problem here.
I will see if I can make that happen.

@cowtowncoder
Copy link
Member

Quick note: realized that use of "static typing" can be used to work around the problem, because that will force use of declared type over attempting to combine declared (static) type with runtime instance type. Although you may or may not want to force it globally (MapperFeature.USE_STATIC_TYPING), it is possible to force it on specific property:

// works on getter or field:
@JsonSerialize(typing = JsonSerialize.Typing.STATIC)
public Map<String, Collection<String>> getRepositoryPrivileges() { ... }

and this will prevent the problem here. So it may be worth considering this as short-term fix; if this is actual type for real use case it should be safe for serialization.

I will still pursue potential change I outlined above, but thought work-around would be useful.

@cowtowncoder cowtowncoder changed the title Failed to specialize base type when serializing to JSON Failed to specialize Map type during serialization where type key type incompatibility overidden via "raw" types Apr 19, 2018
@cowtowncoder cowtowncoder added this to the 2.9.6 milestone Apr 19, 2018
ridoo added a commit to ridoo/series-rest-api that referenced this issue May 30, 2018
Jackson versions newer than 2.9.2 and older than 2.9.6 have problems
with serializing static types:

  FasterXML/jackson-databind#1964

Until v2.9.6 become available we have to live with this workaround
described here

  https://github.com/FasterXML/jackson-
databind/issues/1964#issuecomment-382877148
ridoo added a commit to ridoo/series-rest-api that referenced this issue May 30, 2018
Jackson versions newer than 2.9.2 and older than 2.9.6 have problems
with serializing static types:

  FasterXML/jackson-databind#1964

Until v2.9.6 become available we have to live with this workaround
described here

  https://github.com/FasterXML/jackson-
databind/issues/1964#issuecomment-382877148
@jhoanmanuelms
Copy link

Was this actually fixed? I'm using 2.10.4 and still having the same issue 😕

@cowtowncoder
Copy link
Member

@jhoanmanuelms The reported issue as per gist. If you are experiencing problems, please file a new issue so we can reproduce it and see what is going on. Even if exception looks the same does not necessarily mean the root cause is the same; usually there is some variation.

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

8 participants