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 using @JsonPropertyOrder with any-property (@JsonAnyGetter) #4396

Draft
wants to merge 23 commits into
base: 2.17
Choose a base branch
from

Conversation

JooHyukKim
Copy link
Member

@JooHyukKim JooHyukKim commented Feb 22, 2024

(For after Property Introspection Rewrite)

This PR resolves...

1.. #4388
2.. part of #2592
- AnyGetter with JsonInclude + JsonFilter combo

Modifications

This PR basically...

  • Starts to handle Any-Getter property as regular property (for serialization only).
  • Makes AnyGetterWriter extend BeanPropertyWriter.

notes

  1. If and only if direction is not off.... will clean up and refactor. Solution currently seems a bit of refactoring.
  2. This might allow "ordering" and "ignoring" within AnyGetter prop (I haven't found time to check yet tho)
  3. Change might be to big that maybe

@cowtowncoder
Copy link
Member

One quick note: I think ordering within "any-getter" group can be out of scope as users can use specific Maps to enforce iteration order, and since it is probably more important to be able to locate group itself.
But I think that's what is being done here, just mentioning it.

Otherwise, I'll have to read this couple of times. I guess it would make sense to try to make any-getter placeholder work similar to "simple" properties, so sorting works. But I wonder if there are complications wrt attempts to link/unlink, as well as ignoral. This because formerly "name" of any-getter hasn't had any effect and could not have accidentally ignored (for example) by a regular property with same name having @JsonIgnore.

@JooHyukKim
Copy link
Member Author

JooHyukKim commented Feb 24, 2024

But I wonder if there are complications wrt attempts to link/unlink, as well as ignoral. This because formerly "name" of any-getter hasn't had any effect and could not have accidentally ignored (for example) by a regular property with same name having @JsonIgnore.

Valid point with respect to @JsonIgnore and stuff, so I added tests...

  • On working @JsonIgnore and @JsonIgnoreProperties via 78eb9d5.
  • On @JsonPropertyOrder explicitly on any-getter property works via 8305a0b

Could you explain a bit more on link/unlink?

@cowtowncoder
Copy link
Member

Link/unlink: different getter/setter methods, fields, get "linked" if they have same logical name, either implicit (default) or explicit (rename), into a single logical property.
So if such name that an any-getter happens to have matches a regular property, regular property might be annotated with @JsonIgnore (or maybe any-getter) -- and so the whole property is ignored (none of accessors used).

So all I am wondering is if there was a case like:

class Stuff {
  @JsonAnyGetter
  Map<String, Object> metadata;

  @JsonIgnore
  public String getMetadata() { // unrelated to any-setter
     ...
  }
}

where any-setter would be ignored due to name match. Or something similar related to accidentally matching names.

@JooHyukKim
Copy link
Member Author

Fortunately, seems to work wrt link/unlink-ed properties 👍🏼. Added more tests via 85ae52e.

for (int i = 0; i < _props.length; i++) {
BeanPropertyWriter prop = _props[i];
if (prop instanceof AnyGetterWriter) {
((AnyGetterWriter) prop).resolve(provider);
Copy link
Member

Choose a reason for hiding this comment

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

Why doe we need this, when there's resolution right after? Or conversely, is the resolve call below needed at all?

Copy link
Member Author

Choose a reason for hiding this comment

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

You are right, _anyGetterWriter is no longer needed. Removed _anyGetterWriter entirely 👍🏼

for (int i = 0; i < _props.length; i++) {
BeanPropertyWriter prop = _props[i];
if (prop instanceof AnyGetterWriter) {
((AnyGetterWriter) prop).resolve(provider);
Copy link
Member Author

Choose a reason for hiding this comment

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

You are right, _anyGetterWriter is no longer needed. Removed _anyGetterWriter entirely 👍🏼

Comment on lines +266 to +278
@Test
public void testPrivateAnyGetter() throws Exception {
PrivateAnyGetterPojo pojo = new PrivateAnyGetterPojo();
pojo.add("secondProperty", 2);
String json = MAPPER.writeValueAsString(pojo);

assertEquals(a2q("{" +
"'firstProperty':1," +
"'thirdProperty':3," +
"'forthProperty':4," +
"'secondProperty':2}"), // private accesor, wont' work here
json);
}
Copy link
Member Author

Choose a reason for hiding this comment

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

Here we have test case where any-getter is private without proper getter to be detected for sorting, so "secondProperty" field will be appended in the end.

@@ -457,7 +453,31 @@ protected JsonSerializer<Object> constructBeanOrAddOnSerializer(SerializerProvid
PropertyName name = PropertyName.construct(anyGetter.getName());
BeanProperty.Std anyProp = new BeanProperty.Std(name, valueType, null,
anyGetter, PropertyMetadata.STD_OPTIONAL);
builder.setAnyGetter(new AnyGetterWriter(anyProp, anyGetter, anySer));

Copy link
Member Author

Choose a reason for hiding this comment

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

Since we are now handling props to either replace or add any-getter, I was thinking maybe instead of....

if (anyGetter != null) {
         // 40+ lines of code
         // many more
}

... this, we can do... ..

     if (anyGetter != null) {
          props = updateAnyGetterProp(props);
     }

... like this way, simliar to collectAll() from POJOPropertiesCollector class (Or maybe I am thinking too far at this point 🤔

@@ -218,6 +218,8 @@ public void serializeAsField(Object pojo, JsonGenerator jgen,
writer.serializeAsField(pojo, jgen, provider);
} else if (!jgen.canOmitFields()) { // since 2.3
writer.serializeAsOmittedField(pojo, jgen, provider);
} else if (writer instanceof AnyGetterWriter) {
Copy link
Member Author

Choose a reason for hiding this comment

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

WIP : Contemplating on how should we optimize this check...

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.

None yet

2 participants