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

Allow @JsonMerge with Custom List #4784

Merged
merged 22 commits into from
Nov 12, 2024
Merged

Conversation

JooHyukKim
Copy link
Member

To address #4783

Hopefully it's okay to add to 2.18

@JooHyukKim
Copy link
Member Author

Feel free to close! Wrote a PR in case we might want to add this to test cases,

@cowtowncoder
Copy link
Member

As per my comments elsewhere, I don't think mapper.readerForUpdating() should be needed in this case. Unless existing merge tests has something that work around the issue, different from case here.

@JooHyukKim JooHyukKim closed this Nov 6, 2024
@JooHyukKim JooHyukKim reopened this Nov 6, 2024
@JooHyukKim
Copy link
Member Author

You are right. @JsonMerge with custom Collection type does not work.
Modified test accordingly.

I will probably investigate how different between JDK/custom collection case differ, probably at below point to be specific.

image

@cowtowncoder
Copy link
Member

Note _mapAbstractCollectionType simply maps things like Collection/List -> ArrayList, Set -> LinkedHashSet and so on, to have concrete type to deserialize.
Custom type wouldn't map. But we can't resolve it by mapping; rather would need to figure out how to postpone failure so that lack of deserializer would not trigger exception for this particular @Merge case. Not sure how, but that'd be the idea.

It might be tricky, come to think of it, as there's need for a deserializer to handle merging, I think. Construction of which should not fail unless... hmmh.

@JooHyukKim JooHyukKim marked this pull request as draft November 6, 2024 23:08
@JooHyukKim JooHyukKim marked this pull request as ready for review November 8, 2024 13:45
@JooHyukKim JooHyukKim changed the title Test @JsonMerge with Custom List Allow @JsonMerge with Custom List Nov 8, 2024
throw new IllegalArgumentException("Cannot find a deserializer for non-concrete Collection type "+type);
// [databind#4783] Since 2.19, Allow `@JsonMerge` with Custom Collection extension.
// Let's not throw exceptino so instantiation can happen downstream
if (_canMapSuperInterfaceAsAbstractCollectionType(type, config)) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually... while I understand the intent here, I don't think this check buys as anything: we already know type implements Collection, which is mappable.

So how about this: looking at what AbstractDeserializer.constructForNonPOJO(beanDesc); maybe we should just always construct that -- so remove check for

if (type.getTypeHandler() == null) {

and just do it if we cannot map abstract type. And see how that works wrt our test suite and issue here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried actually, it fails with below exception.
I checked what gets created inside AbstractDeserializer and almost nothing to actually deserialize anything.
The intent behind the // do nothing current approach was to create StringCollectionDeserializer / CollectionDeserializer below starting at line 862.

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.fasterxml.jackson.databind.tofix.JsonMergeWithCustomCollection4783Test$MyListCustom` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 11] (through reference chain: com.fasterxml.jackson.databind.tofix.JsonMergeWithCustomCollection4783Test$MergeCustomStrin
	```

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But wouldn't hurt showcasing here! Lemme just make the suggested change so we both can see

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, you can see checks are failing with the same exceptions.

@JooHyukKim
Copy link
Member Author

JooHyukKim commented Nov 8, 2024

Took a shot at solving this issue.
Added comment🔗 at the implementation.
PTAL when you have time? @cowtowncoder

PS: Looking at current implementation I am not sure if we have this merged to 2.18 (seems a bit risky?)

public class JsonMergeWithCustomCollection4783Test
{

private static class MyArrayListJDK<T> extends ArrayList<T> implements List<T> { }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for implements List<T>, already does via ArrayList<T>.

static class MergeCustomStringList {
@JsonMerge
@JsonProperty
public MyListCustom<String> values = createCustomStringList();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think helper method needed? Just create directly with new MyArrayListCustom<>()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless you want to add values in this method?

{ values.add("a"); }
}

static <T> MyListCustom<T> createCustomStringList() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really needed as per above (but if it was, should parameterize with String to align with name.

static class MergeMyCustomLongList {
@JsonMerge
@JsonProperty
public MyCustomLongList<String> values = createCustomLongList();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmh. If its "Long" list, shouldn't it contain Long values?

Copy link
Member

@cowtowncoder cowtowncoder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so, I think we can simplify the fix by using AbstractDeserializer for all cases, and see how that works.

@cowtowncoder
Copy link
Member

cowtowncoder commented Nov 8, 2024

Took a shot at solving this issue. Added comment🔗 at the implementation. PTAL when you have time? @cowtowncoder

PS: Looking at current implementation I am not sure if we have this merged to 2.18 (seems a bit risky?)

I agree, bit risky for 2.18, based on experience. So let's target 2.19

EDIT: I notice pr is against 2.18. We can consider that if necessary.

@JooHyukKim
Copy link
Member Author

JooHyukKim commented Nov 9, 2024

EDIT: I notice pr is against 2.18. We can consider that if necessary.

I didn't think this was an urgent issue actually, so leaning toward 2.19.
The only reason PR is against 2.18 is that we can make a choice move to 2.19 not the other way :))

I was even thinking to close(to postpone) this one since you got so much going on now @cowtowncoder

@cowtowncoder
Copy link
Member

@JooHyukKim Ok thanks. I think we are good -- let's try getting this done, I think it is a valid problem to solve. I'll see why the test failures occur...

@cowtowncoder
Copy link
Member

Ahhh. Ok, so that'd be too easy -- things won't work because AbstractDeserializer does not override

public T deserialize(JsonParser p, DeserializationContext ctxt, T intoValue)

and it can't really, since we need method like one from CollectionDeserializer

@Override
public Collection<Object> deserialize(JsonParser p, DeserializationContext ctxt,
        Collection<Object> result)

for merging to actually work. So basically we need a proper CollectionDeserializer that... hmmh.

Let me try a simple change to see what happens if we simply remove the check for abstract altogether.

@cowtowncoder
Copy link
Member

Well look at that -- tests actually pass. But probably need a test for non-merging case, to see how error handling works (when trying to deserialize abstract collection without merging)

@cowtowncoder
Copy link
Member

@JooHyukKim Ok so it appears fix might be surprisingly simple: just basically remove the "is it abstract -> fail" check. Love when this happens. :)

@cowtowncoder
Copy link
Member

Due to simplicity of the fix, I think 2.18 might be fine after all.

@JooHyukKim
Copy link
Member Author

@JooHyukKim Ok so it appears fix might be surprisingly simple: just basically remove the "is it abstract -> fail" check. Love when this happens. :)

Interestinnggg :)))
Thank you for the improvement with additional test and all 👌🏼👌🏼
Fixed test class name typo, added release notes and such.

@cowtowncoder
Copy link
Member

Minor tweak: remove change wrt Map defaulting: will do that for 2.19, to reduce risk for 2.18.2.

@cowtowncoder cowtowncoder merged commit 9cd0d26 into FasterXML:2.18 Nov 12, 2024
7 checks passed
@JooHyukKim JooHyukKim deleted the test-4783 branch November 12, 2024 04:00
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

Successfully merging this pull request may close these issues.

2 participants