You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Input types with custom scalars in method field resolver #198
I'm trying to use Input types to a mutation with custom scalars (the ExtendedScalars.Date from graphql-java-extended-scalars). It fails with an error from Jackson (Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of java.time.LocalDate (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)). From what I see this is not currently supported, and probably a bug?
When resolving fields for this method, it calls the Jackson object mapper at
The issue, if I understand correctly, is that the value object that is sent to Jackson already contains a proper LocalDate object (the scalar coercing from JSON is already done), and is not a JSON-representation of the input value. The ObjectMapper fails because it builds a TokenBuffer from the value (as it expects it to be holding JSON). Then it fails as it tries to deserialize back into a new LocalDate...
changed the title [-]Input types with custom scalars in methold field resolver[/-][+]Input types with custom scalars in method field resolver[/+]on Oct 25, 2018
@henrist Since we're talking about a custom scalar I wouldn't say it's a bug. Although a lot of people probably have this use case, so we might think about making it a bit easier to work with.
You'll have to configure the ObjectMapper that's used for supporting dates. This does the trick for me:
@oliemansm Thanks for following up! I'll try out the workaround.
I do have concerns for the lack of proper type constraints around this. An issue I discovered was that normal arguments to root resolver methods were mapped between types (due to the same reason as this GH-issue). I had two scalars/data classes with similar structures, and accidentally I put the wrong one in the function signature, but due to Jackson, the scalar that mapped to type 1 was mapped to type 2 by Jackson instead of being stopped due to different types.
To say it another way: Jackson will do what it takes to convert whatever object is sent to the resolver to the type in the resolver signature. I would expect Jackson to only be effective for mapping JSON-structured data (maps, lists, json-primitives), not arbitrary types (e.g. scalars that are already mapped).
@henrist Just did a test without that additional configuration, just as is. Using the scalars from donbeave/graphql-java-datetime. And using the same test schema from your original description. When I add a breakpoint in the relevant Coercing I see that it is in fact using that to parse the date from the input object. So not Jackson.
@oliemansm The Coercing is working properly. The issue is when this object is then mapped to the argument of the resolver (the code I linked to). When the object is the proper one (e.g. both the coercing gives a LocalDate, and the argument takes an LocalDate), the object is passed through. This happens in the mapper.convertValue call. When there is a mismatch the object will be serialized to a json tokenizer and mapped onto the target type.
I'll try to set up a small example repo in one of next days to show what I mean.
The value list actually contains properly handled and converted scalars of types A and B, but trying to map them to MyInput via Jackson fails. Internally ObjectMapper._convert basically does deserialize(serialize(obj), type), and the second step fails. I can't use any date parser hacks, since my scalars are not dates. Any ideas except for coding a completely custom jackson deserializer?
I use custom scalar in my input type - scalar serializes to a string. I expected that I need to register my custom serializer/deserializer and this is it. It turned out that I need to register them with both GraphQL and Jackson which surprised me, because they can't both serialize/deserialize the same thing.
I analyzed what is happening internally and it seems the flow is as follows:
Coercing.parseValue(String) -> MyCustomScalar
JsonSerializer.serialize(MyCustomScalar)
JsonDeserializer.deserialize() -> MyCustomScalar
So my scalar is first deserialized (GraphQL), then serialized (Jackson) and deserialized again (Jackson). It does not seem to be right :-)
We ran into something similar where we have a list of scalars as input argument.
Basically what @brutall said, the GraphQL library already parsed the scalar type into an object.
Then the methodFieldResolver uses the objectMapper to convert that object into an object of that same type... But it could just return the value without converting!
Maybe it's possible to add a check for scalar input types or optionals/lists of scalar types and skip the mapper.convertValue call?
I can confirm that this issue is still there in the latest version of graphql-java-tools.
The comments here and in #424 and #436 describes the issue really well so i couldn't add anything more. If anyone knows any work arounds to deal with this when using a third party library class, please let us know.
Activity
[-]Input types with custom scalars in methold field resolver[/-][+]Input types with custom scalars in method field resolver[/+]oliemansm commentedon Nov 4, 2018
@henrist Since we're talking about a custom scalar I wouldn't say it's a bug. Although a lot of people probably have this use case, so we might think about making it a bit easier to work with.
You'll have to configure the
ObjectMapperthat's used for supporting dates. This does the trick for me:You pass in this
ObjectMapperthrough theSchemaParserOptions:henrist commentedon Nov 4, 2018
@oliemansm Thanks for following up! I'll try out the workaround.
I do have concerns for the lack of proper type constraints around this. An issue I discovered was that normal arguments to root resolver methods were mapped between types (due to the same reason as this GH-issue). I had two scalars/data classes with similar structures, and accidentally I put the wrong one in the function signature, but due to Jackson, the scalar that mapped to type 1 was mapped to type 2 by Jackson instead of being stopped due to different types.
To say it another way: Jackson will do what it takes to convert whatever object is sent to the resolver to the type in the resolver signature. I would expect Jackson to only be effective for mapping JSON-structured data (maps, lists, json-primitives), not arbitrary types (e.g. scalars that are already mapped).
oliemansm commentedon Nov 5, 2018
@henrist I get your point. Will try to debug a bit to see what's going on.
Is your concern in the first paragraph only about date time icw Jackson or is it broader related to
graphql-java-tools?oliemansm commentedon Nov 5, 2018
@henrist Just did a test without that additional configuration, just as is. Using the scalars from donbeave/graphql-java-datetime. And using the same test schema from your original description. When I add a breakpoint in the relevant
CoercingI see that it is in fact using that to parse the date from the input object. So not Jackson.henrist commentedon Nov 6, 2018
@oliemansm The
Coercingis working properly. The issue is when this object is then mapped to the argument of the resolver (the code I linked to). When the object is the proper one (e.g. both the coercing gives a LocalDate, and the argument takes an LocalDate), the object is passed through. This happens in themapper.convertValuecall. When there is a mismatch the object will be serialized to a json tokenizer and mapped onto the target type.I'll try to set up a small example repo in one of next days to show what I mean.
oliemansm commentedon Nov 6, 2018
@henrist An example would be very useful, thanks.
b4eEX commentedon Dec 3, 2018
@oliemansm I have an input type that looks like
, that fails on the same issue with Jackson. The offending method is
com.coxautodev.graphql.tools.MethodFieldResolver.createDataFetcher:return@add mapper.convertValue(value, typeReference)The
valuelist actually contains properly handled and converted scalars of typesAandB, but trying to map them toMyInputvia Jackson fails. InternallyObjectMapper._convertbasically doesdeserialize(serialize(obj), type), and the second step fails. I can't use any date parser hacks, since my scalars are not dates. Any ideas except for coding a completely custom jackson deserializer?Herlevsen commentedon Dec 15, 2018
@henrist Did you figure out anything? I am having the same problem
broo2s commentedon May 30, 2019
So I'm pretty confused with this issue.
I use custom scalar in my input type - scalar serializes to a string. I expected that I need to register my custom serializer/deserializer and this is it. It turned out that I need to register them with both GraphQL and Jackson which surprised me, because they can't both serialize/deserialize the same thing.
I analyzed what is happening internally and it seems the flow is as follows:
So my scalar is first deserialized (GraphQL), then serialized (Jackson) and deserialized again (Jackson). It does not seem to be right :-)
arian commentedon Oct 10, 2019
We ran into something similar where we have a list of scalars as input argument.
Basically what @brutall said, the GraphQL library already parsed the scalar type into an object.
Then the methodFieldResolver uses the objectMapper to convert that object into an object of that same type... But it could just return the value without converting!
Maybe it's possible to add a check for scalar input types or optionals/lists of scalar types and skip the
mapper.convertValuecall?Fixes graphql-java-kickstart#198 Add support for lists of Scalar fiel…
Merge pull request #320 from symbaloo/198-scalar-input-types
pramod-knidal commentedon Feb 13, 2021
I can confirm that this issue is still there in the latest version of
graphql-java-tools.The comments here and in #424 and #436 describes the issue really well so i couldn't add anything more. If anyone knows any work arounds to deal with this when using a third party library class, please let us know.
vojtapol commentedon Feb 17, 2021
@pramodshri-tgsys We would also welcome if you could propose a solution to this.
pramod-knidal commentedon Feb 19, 2021
As noted by brutall here, not going through the process of serializing and deserializing seems to be the right solution to me.