17

I'm writing code that needs to access the list of properties of a class as defined by a Jackson configuration.

For instance, for the following class:

@JsonIgnoreProperties(value = { "intValue" })
public class MyDto {

    @JsonProperty("name")
    private String stringValue;

    private int intValue;

    private long longValue;

    @JsonIgnore
    private boolean booleanValue;

    // standard setters and getters are not shown
}

I would get [name,longValue] because that's the properties that Jackson actually considers when serializing.

I don't think writing a whole piece of code to look for getters/setters and inspect Jackson annotations is the way to go, since that would be reimplementing Jackson.

If I'm able to get a handle on the Jackson ObjectMapper used for serialization, is there a way to get a list of properties of a Class<?> or Type object? (respecting Jackson annotations and config)

I dug a bit into Jackson's implementation, and found the POJOPropertiesCollector, but I'm not sure about how I can use it from outside Jackson (we're not supposed to do this I believe).

As a last resort, I could create an instance of the class I'm inspecting, serialize it with the ObjectMapper, and then parse the JSON to find property names, but I don't think this is clean either (and it would bring its whole set of probelms: nulls might not be serialized, what happens in the construtor etc.).

Any ideas?

Joffrey
  • 32,348
  • 6
  • 68
  • 100

1 Answers1

29

With Jackson, you can introspect an arbitrary class to get the available JSON properties:

// Construct a Jackson JavaType for your class
JavaType javaType = mapper.getTypeFactory().constructType(MyDto.class);

// Introspect the given type
BeanDescription beanDescription = mapper.getSerializationConfig().introspect(javaType);

// Find properties
List<BeanPropertyDefinition> properties = beanDescription.findProperties();

The BeanPropertyDefinition list should give you the details you need regarding the JSON properties.


The @JsonIgnoreProperties class level annotation is not taken into account with the above mentioned approach. But you can use an AnnotationIntrospector to get the properties ignored on class level:

// Get class level ignored properties
Set<String> ignoredProperties = mapper.getSerializationConfig().getAnnotationIntrospector()
        .findPropertyIgnorals(beanDescription.getClassInfo()).getIgnored();

Then filter properties removing the properties which are present in ignoredProperties:

// Filter properties removing the class level ignored ones
List<BeanPropertyDefinition> availableProperties = properties.stream()
        .filter(property -> !ignoredProperties.contains(property.getName()))
        .collect(Collectors.toList());

This approach works even if you have mix-ins defined for your class.


The AnnotationIntrospector#findPropertyIgnorals(Annotated) method was introduced in Jackson 2.8. The AnnotationIntrospector#findPropertiesToIgnore(Annotated, boolean) method can be used for older versions (but it's deprecated since Jackson 2.8).

cassiomolin
  • 124,154
  • 35
  • 280
  • 359
  • 1
    This looks very promising, I will give it a try – Joffrey Aug 23 '17 at 12:37
  • 1
    It almost does the job. It just fails to ignore properties that are listed in a `@JsonIgnoreProperties` class annotation. `@JsonIgnore` field annotation is correctly taken into account, though. I have a feeling that class annotations in general are ignored, but need more experimentation to confirm this. – Joffrey Aug 23 '17 at 16:08
  • @Joffrey That's true. Looks like the `@JsonIgnoreProperties` annotation is not taken into account. I have updated my answer. – cassiomolin Aug 23 '17 at 22:39
  • 2
    Thanks for the update, this is actually close to what I had ended up doing after digging a bit more. Sorry not to have posted it earlier. For future reference, here is my full implementation: https://github.com/joffrey-bion/livedoc/blob/master/livedoc-springmvc/src/main/java/org/hildan/livedoc/springmvc/scanner/properties/JacksonPropertyScanner.java – Joffrey Aug 30 '17 at 08:00
  • Up-to-date link: https://github.com/joffrey-bion/livedoc/blob/master/livedoc-core/src/main/java/org/hildan/livedoc/core/scanners/properties/JacksonPropertyScanner.java – Joffrey Oct 29 '18 at 13:13
  • would be improved by including a line of where "mapper" comes from, both for this and for the linked answer. – Ben Barden Sep 18 '20 at 20:07
  • 2
    @BenBarden the premise of the question is that I already have access to an `ObjectMapper` so there is no need to explain how to get it – Joffrey Apr 29 '21 at 21:05
  • @Joffrey sure... and then there are the people who come along behind you and are trying to figure this stuff out... as I was when I encountered the Q/A and wrote that comment. It's great that the answer provided for your needs, but you're not the only one who will be looking to it for help. Thus "would be improved". – Ben Barden Apr 29 '21 at 21:15
  • @BenBarden I see your point, I just believe that reading the big bold part summarizing the question is somewhat expected from the reader (about as much as reading the question's title). Especially if the reader is confused about the sample code in the answer. A lot of answers take the question's code as basis. – Joffrey Apr 30 '21 at 08:09
  • @Joffrey ...but the fact that you had the ObjectMapper in hand doesn't mean that the next person who searches for "get the list of properties of a class as Jackson views it" necessarily does. – Ben Barden Apr 30 '21 at 13:26
  • 2
    @BenBarden while I agree with you in the general case, I don't think this applies here. The ObjectMapper is sort of the entry point of the Jackson library. So if a person interacts with Jackson this is usually via an ObjectMapper. If not, the way to get the ObjectMapper will be framework specific (for instance in the context of Spring, it might be via injection of the relevant bean). So we can't really add anything here besides a generic comment saying to get an ObjectMapper from somewhere. The way to get it from a specific framework will be the object of a separate question and answer. – Joffrey Apr 30 '21 at 19:26
  • @BenBarden `new ObjectMapper()` – Sam Gammon Aug 19 '21 at 23:20