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

Got UnrecognizedPropertyException when the field name has a capital letter in second position #533

Open
maximevw opened this issue Jan 30, 2025 · 1 comment

Comments

@maximevw
Copy link

maximevw commented Jan 30, 2025

Hello,

I'm using Jackson (version 2.18.2, through quarkus-jackson 3.17.8 + additional dependencies to jackson-dataformat-yaml and jackson-datatype-jsr310) to parse a YAML file like this one:

fields:
  - name: myField
    section:
        xStart: 10
        yStart: 20

I have the following Java objects (I use Lombok annotations):

@Data
public class PageDescriptor {
  private List<FieldDescriptor> fields;
}
@Data
public class FieldDescriptor {
  private String name;
  private Section section;
}
@Data
public class Section {
  private int xStart;
  private int yStart;
}

When I try to deserialize the YAML file into a Java object of type PageDescriptor using the code below:

ObjectMapper ymlMapper = new ObjectMapper(new YAMLFactory());
ymlMapper.findAndRegisterModules();
PageDescriptor pageDescriptor = ymlMapper.readValue(getClass().getClassLoader().getResource("test.yaml"), PageDescriptor.class);

I got this error:

Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "xStart" (class com.test.Section), not marked as ignorable (4 known properties: "xstart", "ystart"])
 at [Source: (URL); line: 45, column: 22] (through reference chain: com.test.PageDescriptor["fields"]->java.util.ArrayList[0]->com.test.FieldDescriptor["section"]->com.test.Section["xStart"])
        at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:61)
        at com.fasterxml.jackson.databind.DeserializationContext.handleUnknownProperty(DeserializationContext.java:1153)
        at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:2241)
        at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1821)
        at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1799)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:316)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:177)
        at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:310)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:177)
        at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer._deserializeFromArray(CollectionDeserializer.java:361)
        at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:246)
        at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:30)
        at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:310)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:177)
        at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer._deserializeFromArray(CollectionDeserializer.java:361)
        at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:246)
        at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:30)
        at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:310)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:177)
        at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:342)
        at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4917)
        at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3791)
        at com.test.GenerationService.generate(GenerationService.java:80)
        at com.test.GenerationService_ClientProxy.generate(Unknown Source)
        at com.test.GenerationService_Observer_Synthetic_jMJAdbQB0LYH0ZTEbOid3CLUQ6Y.notify(Unknown Source)
        at io.quarkus.arc.impl.EventImpl$Notifier.notifyObservers(EventImpl.java:351)
        ... 17 more

If I modify the yaml file to replace xStart and yStart by xstart and ystart respectively, the parsing works correctly.
If I rename the field xStart into xxStart, for example, into my class Section, I'm able to parse correctly the field xxStart in the yaml file.

I don't know if the root cause of the bug is in this project or in jackson-databind one.

@cowtowncoder
Copy link
Member

cowtowncoder commented Jan 31, 2025

This is a known problem with field names like xStart, Lombok & Jackson. You need to use @JsonProperty to indicate proper casing here unfortunately.
Issue is not specific to YAML module.

The problem is that accessor (getter) generated has name that does not translate to xStart -- it is due to Lombok basing things on field name (creating accessor names), and Jackson starting from accessor. And for this one particular case (well, leading upper-case would have similar issue) expected names do not match, and cannot be determined as things are.

In future it might be possible to address this in jackson-databind either by allowing case-insensitive matching of possible names b/w Fields and Getters/Setters, or by allowing detection to start with Fields as starting point.
But this is not going to be short-term thing to do, which is why I suggest use of @JsonProperty for specific properties.

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

No branches or pull requests

2 participants