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

Adding Supports for Records (Groovy) + Passing Record Test. #122

Closed

Conversation

Shounaks
Copy link
Contributor

Issue : support for records #94

This will enable the introspector to catch Groovy generated getters, and serialize the records.

@@ -142,6 +143,13 @@ private static void _introspect(Class<?> currType, Map<String, PropBuilder> prop
name = decap(name.substring(2));
_propFrom(props, name).withIsGetter(m);
}
} else {
// This will allow getters with field name as their getters, like the ones generated by Groovy
boolean isDirectNameGetter = Arrays.stream(currType.getDeclaredFields()).map(Field::getName).anyMatch(f -> f.equals(m.getName()));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

i believe if we get a list of fields & check if there is a field with the same name as current method, then we need to add it as a prop

, this can be optimized, but as of now, its working.

Copy link
Member

Choose a reason for hiding this comment

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

I think this capability needs to be behind Json.Feature flag since while it makes sense wrt Records (and probably other value types), it can also be unexpected for other users. That is, it changes compatibility.

Other than that it'd make sense to build a map from name to Field in loop above, to avoid linear scanning for every potential "non-get-getter".

@Shounaks Shounaks force-pushed the 2.17-add-failing-test-for-records branch from eb9e69e to a039df4 Compare February 22, 2024 17:32
@cowtowncoder
Copy link
Member

Quick note: if this is meant for Groovy records specifically, should probably file a new separate issue -- #94 is, I think, for Java 17 Record type and require immutability including construction by multi-argument constructor.
This seems to be just for automatically recognizing "non-get getters" (so, name() for property "name" if (and only if) there is matching Field), which would also work for Java Records, but wouldn't be full Record support in my mind.

I also think this should be behind a new JSON.Feature (DETECT_FIELD_MATCHING_GETTERS? or some better name, ideally :) ) just because it is behavioral change that might not be ideal for some users.
(I think I'd be ok but it defaulting to true, just as long as it is possible to disable it for compatibility reasons).

@@ -0,0 +1,68 @@
package com.fasterxml.jackson.jr.passing
Copy link
Member

Choose a reason for hiding this comment

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

Is the new passing needed to avoid split packages or... ?

If necessary, maybe use com.fasterxml.jackson.jr.ob.groovy.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

my mistake, removed in latest commits.

@Shounaks
Copy link
Contributor Author

Yes, this will fix Java14's Records as well as Groovy Records.

This seems to be just for automatically recognizing "non-get getters" (so, name() for property "name" if (and only if) there is matching Field), which would also work for Java Records, but wouldn't be full Record support in my mind.

Okay so, how should we go on to check which object is a record and which is not, without having access to native internal methods?

I tried to do some digging, and found out this:
image
but we dont really support JDK 16..
image
and i couldnt get to the end of how this native method is implemented. (no experience in this field sadly..)

One immediate way that i can think of is to check constructor parameters matching the field names .
image
but i am not sure how it will interact with current code. Might need your help here @cowtowncoder .

@cowtowncoder
Copy link
Member

cowtowncoder commented Feb 24, 2024

It is possible to detect "Record-ness" of a type, even from JDK 8: jackson-databind does that. Actually detecting something is Record is relatively easy: src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java: has this:

    public static boolean isRecordType(Class<?> cls) {
        Class<?> parent = cls.getSuperclass();
        return (parent != null) && "java.lang.Record".equals(parent.getName());
    }

which we could re-purpose. Same might work for Groovy types.

Anything fancier (finding canonical constructor, declared properties) gets trickier, but just knowing something is a Record type is quite doable.

@cowtowncoder
Copy link
Member

I think that we have a choice, so either:

  1. Use a JSON.Feature to enable detection, regardless of type (whether it's Record or not)
  2. Auto-detect Records and apply different rules

I guess one could combine things too, to basically default to auto-detecting via (2), but allowing disabling by (1). Not sure how necessary that is.

@cowtowncoder
Copy link
Member

Ok, couple of additional notes:

  1. Groovy test seems to fail for some reason in CI
  2. Not sure how to test Java Records: jackson-databind uses different profiles to optionally included Java 17+ tests, that is a possibility -- but baseline for Jackson 2.x jackson-jr is JDK 8 so they can't be added in main src/test/java
  3. ... although, since jackson-jr is multi-module Maven project, it potentially could have separate test-only package that runs after building jr-objects. Either way, gets bit complicated

We could, however, also test case of "not Record but has field-name matching getter methods" as surrogates for Records. This assuming we do not want to limit this feature to just Java/Groovy Record types.

@Shounaks
Copy link
Contributor Author

I think that we have a choice, so either:

  1. Use a JSON.Feature to enable detection, regardless of type (whether it's Record or not)
  2. Auto-detect Records and apply different rules
    I guess one could combine things too, to basically default to auto-detecting via (2), but allowing disabling by (1). Not sure how necessary that is.

I believe 1st choice is better, since we have the knob to turn this feature off, when working with other libraries,

@Shounaks Shounaks force-pushed the 2.17-add-failing-test-for-records branch from 9cc4bfc to 53b9bde Compare February 27, 2024 08:22
@Shounaks
Copy link
Contributor Author

This failing test, it seems, that the since, the getter is public, we are getting the values, and hence we are able to serialize these items. looks like correct behavior. can you confirm once again? so that I will remove this test. or this is the expected behavior?

I didnt really think about JsonAutoDetect.Visibility.PROTECTED_AND_PUBLIC when implementation of this feature. how should these features interact with that?

@@ -145,6 +149,15 @@ private static void _introspect(Class<?> currType, Map<String, PropBuilder> prop
name = decap(name.substring(2));
_propFrom(props, name).withIsGetter(m);
}
} else if(USE_FIELD_NAME_GETTERS.enabledByDefault()){
Copy link
Member

@cowtowncoder cowtowncoder Feb 29, 2024

Choose a reason for hiding this comment

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

As with other comment, enabledByDefault() is not to be used here -- we need actual configuration settings.

There's an example earlier on:

        final boolean noStatics = JSON.Feature.INCLUDE_STATIC_FIELDS.isDisabled(features);

that shows expected usage.

@cowtowncoder
Copy link
Member

Quick request: would it be possible to extract change to add new test module for Groovy classes into separate PR? We already added jr-objects/src/test/groovy/com/fasterxml/jackson/jr/GroovyTest.groovy and that could now be moved to the new module; and that PR could be merged before figuring out details of actual feature.

@Shounaks
Copy link
Contributor Author

Shounaks commented Mar 1, 2024

sounds good, let me do that.

@Shounaks
Copy link
Contributor Author

Shounaks commented Mar 1, 2024

Quick request: would it be possible to extract change to add new test module for Groovy classes into separate PR? We already added jr-objects/src/test/groovy/com/fasterxml/jackson/jr/GroovyTest.groovy and that could now be moved to the new module; and that PR could be merged before figuring out details of actual feature.

Created a new PR: #128

@Shounaks
Copy link
Contributor Author

Shounaks commented Mar 5, 2024

Closing this PR, will start with clean slate, with minimal changes, and formatting.

@Shounaks Shounaks closed this Mar 5, 2024
@Shounaks Shounaks deleted the 2.17-add-failing-test-for-records branch March 10, 2024 08:36
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