-
-
Notifications
You must be signed in to change notification settings - Fork 42
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
Convert GenericType or ResolvedType to JavaType #69
Comments
Correct: It would definitely be nice to have some means to convert I am open to ideas for supporting some means of conversion, with the constraint that no dependencies may be added across these 2 libraries (Jackson, ClassMate). |
+1 for interoperability between Jackson and Classmate (two solid dependencies for me). In my case lots of good introspection is performed by Jackson... then repeated by Classmate. Unless... Is there a way to have Jackson's JavaType tell me about that type's (generic) fields? |
I'd definitely be +1 for improvements, just not sure where and how (plus, if I had time to directly work on it). But as to |
I won't be so bold as to make an ask. After doing some digging on my own I arrived at what you just told me... no easy path. I love and use both libraries and from a purely aspirational perspective I think it would be nice if Classmate was flexible enough so that Jackson used Classmate. But as the saying goes, I'm sure we all have bigger fish to fry. For me (and probably most programs) doing introspection twice (or even five times) on startup doesn't really impact me. Thanks for your contributions! |
Yeah. The origin story of ClassMate is that it came out of Jackson internals, cleaned up. In some ways one would probably need to have custom Such thing -- The bigger approach of making Jackson just use ClassMate is something that ultimately would probably be good (despite adding one more dependency), except for one thing: since |
Thank you for the ClassMate library. I second the need for a conversion from
True and true, but nothing says a support module published as a separate artifact (e.g. In other words, assuming you have a multimodule Maven project, you just add another aggregated subproject with only these conversion utilities, and give the project an artifact ID of |
Actually let me take a step back and address the question itself. I have a It seems the most direct analog of ClassMate
Which one of these is easier—or even possible? Since I'm calling I see that return forType(_config.getTypeFactory().constructType(valueTypeRef.getType())) That uses the Type superClass = getClass().getGenericSuperclass();
_type = ((ParameterizedType) superClass).getActualTypeArguments()[0]; So (not having tried this), could we construct a GenericType<?> genericType = new GenericType<Foo<Bar>>(){}; //just like TypeReference
Type genericTypeSubclassSuperClass = genericType.getClass().getGenericSuperclass();
//TODO do sanity check `genericTypeSubclassSuperClass instanceof Class<?>` if desired
Type unwrappedGenericType = ((ParameterizedType) genericTypeSubclassSuperClass).getActualTypeArguments()[0];
JavaType = _config.getTypeFactory().constructType(unwrappedGenericType); I'm inferring all of this just from perusing the source code—I haven't tested any of it, and I don't even now if it will compile. but it seems reasonably simple and straightforward The only mystery at the moment is that I haven't searched enough to find out how to get a Update: Oh, the Update: Duh 🤦♂️ — the So all indications are that I would be able to create a I'm going to pause work on this for today, but tomorrow I'll test the conversion code above and write unit tests for it. There seems to be no mystery—I would be willing to bet a bagel (to invent an expression) that it will work just fine. |
I couldn't wait until tomorrow. 😄 Here's the code: public static JavaType toJavaType(TypeFactory typeFactory, GenericType<?> genericType) {
Type genericTypeSubclassSuperClass = genericType.getClass().getGenericSuperclass();
//TODO do sanity check `genericTypeSubclassSuperClass instanceof Class<?>` if desired
Type unwrappedGenericType = ((ParameterizedType)genericTypeSubclassSuperClass).getActualTypeArguments()[0];
return typeFactory.constructType(unwrappedGenericType);
} Here's the unit test: @Test
void test() {
@SuppressWarnings("serial")
final GenericType<?> genericType = new GenericType<List<String>>() {};
final TypeReference<?> typeReference = new TypeReference<List<String>>() {};
final TypeFactory typeFactory = TypeFactory.defaultInstance();
final JavaType javaTypeFromTypeReference = typeFactory.constructType(typeReference.getType());
assertThat(toJavaType(typeFactory, genericType), is(javaTypeFromTypeReference));
} I'll create another convenience method: public static ObjectReader forType(ObjectReader objectReader, TypeFactory typeFactory, GenericType<?> genericType) This method will simply delegate to the method above, passing I haven't yet tried the resulting |
(Note that I'm not sure why |
I had another thought. You'll note that the conversion logic I gave above to convert ClassMate public ObjectReader forType(JavaType valueType) { // already exists
…
}
public ObjectReader forType(TypeReference<?> valueTypeRef) { // already exists
…
}
public ObjectReader forSuperTypeToken(Object superTypeToken) { //new method
Type superTypeTokenSuperClass = superTypeToken.getClass().getGenericSuperclass();
//TODO do sanity check `superTypeTokenSuperClass instanceof Class<?>` if desired
Type unwrappedType = ((ParameterizedType)superTypeTokenSuperClass).getActualTypeArguments()[0];
return forType(_config.getTypeFactory().constructType(unwrappedType);
} This should work not only for ClassMate's Of course you would want to add appropriate guard code to ensure the You may decide for whatever reason you don't want to add this sort of feature to Jackson. I'm just pointing out it's possible. |
Agreed on many points: yes, something could definitely depend on ClassMate and Jackson-databind. Similarly, ability to use "generalized" super token pattern seems like an interesting idea. My main concern really (beyond avoiding wrong kind of dependencies) has to do with API design. And I think that instead of adding super-type token into Still, even if something else were to be added in other places, I would love to get a PR for adding resolution method (with simple validation as suggested that there's 1-and-only-1 type parameter, and super type is |
I'm not so worried about users finding the conversion code in In other words, I agree that the conversion code should go in I wouldn't mind doing this. I'm in the middle of some intense coding for another project, though, so I won't get to it immediately. |
A comment here for whoever gets time to address this: I see this note in Jackson Type superClass = getClass().getGenericSuperclass();
if (superClass instanceof Class<?>) { // sanity check, should never happen
throw new IllegalArgumentException("Internal error: TypeReference constructed without actual type information");
} I think the point here is that we expect the generic super type not to be just a plain class, but some parameterized type that contains type information. But I think if checks were added below specifically for that, those would be more direct and include the check here. I'm referring to: /* 22-Dec-2008, tatu: Not sure if this case is safe -- I suspect
* it is possible to make it fail?
* But let's deal with specific
* case when we know an actual use case, and thereby suitable
* workarounds for valid case(s) and/or error to throw
* on invalid one(s).
*/
_type = ((ParameterizedType) superClass).getActualTypeArguments()[0]; Thus the improved, pretty code looks like this: /**
* Converts a ClassMate "generic type" to a Jackson "Java type" using the supplied type factory.
* @param typeFactory The type factory for creating Jackson types.
* @param genericType The ClassMate generic type holder.
* @return A Jackson Java type token instance.
* @throws IllegalArgumentException if the given type is not a direct subclass of {@link GenericType} providing a single generic type parameter.
*/
public static JavaType toJavaType(@Nonnull final TypeFactory typeFactory, @Nonnull final GenericType<?> genericType) {
final Type genericTypeSubclassSuperClass = genericType.getClass().getGenericSuperclass();
if(genericTypeSubclassSuperClass instanceof ParameterizedType parameterizedType) {
checkArgument(GenericType.class.equals(parameterizedType.getRawType()), "Type token must be immediate subclass of `%s`.",
GenericType.class.getSimpleName());
final Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
if(actualTypeArguments.length == 1) {
return typeFactory.constructType(actualTypeArguments[0]);
}
}
throw new IllegalArgumentException("Type token must provide a single generic type argument.");
} This is code specific to |
Right, Java 17 cannot be used yet. Point about I would still start with |
The other issue in my mind is naming; if we have a method that takes a general "super type token" (such as So one approach for the naming would be to simply call it a "type token", and have code that detects whether we are passing a It would look like this: /**
* Returns a type represented by a type token: either a {@link Class}; or a direct subclass of some parameterized type (a <dfn>super type token</dfn>), where
* the super class parameterized type represents the type.
* @param typeToken The type token: either a {@link Class} or a super type token such as <code>com.fasterxml.classmate.GenericType<T></code>, Spring
* <code>org.springframework.core.ParameterizedTypeReference<T>, or Guava <code>com.google.common.reflect.TypeToken<T></code>.
* @return The type represented by the type token.
* @see <a href="https://gafter.blogspot.com/2006/12/super-type-tokens.html">Super Type Tokens</a>
* @throws IllegalArgumentException if the given object is not a {@link Class}; or a direct subclass of a class providing a single generic type parameter.
*/
public static Type typeTokenToType(@Nonnull final Object typeToken) {
if(typeToken instanceof Class<?> clazz) { //a class as a type token is already the type
return clazz;
}
//super type token
final Type superTypeTokenSuperClass = typeToken.getClass().getGenericSuperclass();
if(superTypeTokenSuperClass instanceof ParameterizedType parameterizedType) {
final Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
if(actualTypeArguments.length == 1) {
return actualTypeArguments[0];
}
}
throw new IllegalArgumentException("Type token must be an instance of `Class`, or have a super class providing a single generic type argument.");
} That's the generalized "convert type token to I'll likely add a method like this to my PLOOP library, which we discussed years ago and which will soon come back to life. For Jackson a new public JavaType constructTypeFromTypeToken(final Object typeToken) {
return constructType(typeTokenToType(typeToken));
} Well that was easy! And finally the convenience method for public ObjectReader forTypeToken(final Object typeToken) {
return forType(getConfig().getTypeFactory().constructTypeFromTypeToken(typeToken));
} Then all the following would work:
(I realize that at some point we'll need to open a ticket in Jackson to add this. I can add it when I get time; I'm trying to juggle several things at once as I work on my own project which is using this.) |
Let me come full circle back to the original question. I've already fully explained how to convert from a ClassMate But what about converting from Let me explain it a different way. If I find a If only there were a If there is not, after I pull out my PLOOP library and dust it off, I may improve its |
I agree that converting from But I am not sure integration for Does this make sense? Going with PLOOP probably makes sense then. |
Sure, sounds fine. The biggest thing I was wanting to confirm here (besides thinking out loud) is that there wasn't a In the meantime, if you get a chance to work on any ClassMate things, I would say #74 and #75 should get the highest priority. That way it would be easier for me and/or others to jump in and help with other tickets if needed. |
Correct: there isn't. I am not a fan of I'll see if I find time to tackle issues mentioned. |
Some function like
Jackson2JsonRedisSerializer
requiresJavaType
as parameter. When I tried to pass generic type parameter viaGenericType
object, I find thatGenericType
can only be converted toResolvedType
, and there is no way from ResolvedType to JavaType.The text was updated successfully, but these errors were encountered: