Allow list fields on map-backed types - #829
Merged
Merged
Conversation
Map-backed types could not declare list fields. MapFieldResolver resolves the value type of an untyped map to Object, and the ListType branch of TypeClassMatcher only accepted a parameterized Iterable or an array, so schema building failed with "Java class is not a List or generic type information was lost: class java.lang.Object". Object already matches scalar and object type fields, so a list field backed by Object now matches its element type as Object too. Scalar elements produce a scalar match and object type elements go through the existing dictionary lookup for Object, which still reports a missing dictionary entry. Maps with a typed list value type keep matching through the parameterized Iterable branch. Fixes #295 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Fixes #295
Checklist
Description
A type backed by a
Mapcan't have list fields. With a resolver returningMap<String, Object>, or aHashMapregistered in the dictionary (.dictionary("G1", HashMap.class)in the issue), a field likeb: [String]fails to build withJava class is not a List or generic type information was lost: class java.lang.Object.MapFieldResolverresolves the value type of an untyped map toObject, and sinceFieldResolverScannerpasses it the raw class, that's also the case for aMap<String, Any>. TheListTypebranch ofTypeClassMatcher#matchonly accepts a parameterizedIterableor an array, so it throws. Scalar and object type fields already acceptObject.Now when the real type is
Object,TypeClassMatchermatches the list's element type asObjecttoo. Scalar elements become a scalar match, and nested lists ([[Int]]) recurse the same way. Object type elements go through the existingObjecthandling inSchemaClassScanner, so a type in the dictionary works and a missing one gets the usual "maps to a field of type java.lang.Object however there is no matching entry for this type in the type dictionary" error. Maps with a typed list value, likeclass TagsMap : HashMap<String, List<String>>, still match through the parameterizedIterablebranch.Raw
List/Iterablereturn types (e.g. a Java method returning a rawjava.util.List) still fail with "generic type information was lost". That's the lost generics case, not the map one.FieldResolverScannerstill passes the raw map class toMapFieldResolver, so a method returningMap<String, List<Key>>still resolves its values toObjectand needsKeyin the dictionary. I left that alone since it doesn't matter for scalar lists anymore.Map-typed input objects with a list of input objects (
input FooInput { subs: [SubInput] }with aMap<String, Any>argument andSubInputin the dictionary) also still fail with "Java class is not a List". That's a different path: the dictionary fallback inSchemaClassScanner#handleNewTypereturns the element class but matches it against the list-wrapped type. It's broken on master as well and probably belongs with the input scanning issues (#355, #479, #422). Also, the repro in #437 (a root resolver that extendsHashMap, withfoo: [FooType]) now builds but still fails at runtime withMapFieldResolver attempt to fetch a field from an object instance that was not a map. That's an older bug inMapFieldResolverDataFetcher. It checks the execution root instead of the source, so even scalar fields on such a resolver fail on master.Behaviour change: the fix is in
TypeClassMatcher, so anyObject-typed source now matches a list field, not only map values. That includes resolver methods returningAny/Object,List<*>/List<?>elements against nested lists, andObjectparameters for list arguments. These used to fail with "Java class is not a List or generic type information was lost" and now build, same asObjectalready did for scalar and object type fields. GraphQL object types inside those lists have to be in the dictionary, otherwise the build fails with the existing dictionary error. Schemas that built before aren't affected.🤖 Generated with Claude Code