3

As usual, there is probaly a very simple solution to my problem:

I have a JSON schema snippet that defines the following enum:

"title" : {
    "type": "string",
    "enum": ["Mr", "Miss", "Mrs", "Ms"],
    "description": "The person's title"
 }

My company's framework then uses jsonschema2pojo and maven to create the necessary POJO (Title lives in Clazz, as title is part of clazz in the JSON schema - with clazz as a name being made up - replace it with employee or customer or whatever you like):

Generated POJO

@Generated("org.jsonschema2pojo")
public static enum Title {

    MR("Mr"),
    MISS("Miss"),
    MRS("Mrs"),
    MS("Ms");
    private final String value;
    private static Map<String, Clazz.Title> constants = new HashMap<String, Clazz.Title>();

    static {
        for (Clazz.Title c: values()) {
            constants.put(c.value, c);
        }
    }

    private Title(String value) {
        this.value = value;
    }

    @JsonValue
    @Override
    public String toString() {
        return this.value;
    }

    @JsonCreator
    public static Clazz.Title fromValue(String value) {
        Clazz.Title constant = constants.get(value);
        if (constant == null) {
            throw new IllegalArgumentException(value);
        } else {
            return constant;
        }
    }
}

When I run a request containing the following against it:

...
  "title" : "Mr",
...

I get this error thrown back at me:

com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not construct instance of com.example.foo.representations.jaxb.Clazz$Title from String value 'Mr': value not one of declared Enum instance names: [Mr, Miss, Mrs, Ms]  at [Source: org.apache.cxf.transport.http.AbstractHTTPDestination$1@1a372b7; line: 4, column: 3] (through reference chain: com.example.foo.representations.jaxb.MySchema["Clazz"]->com.example.foo.representations.jaxb.Clazz["title"])

Clearly, "Mr" is in the Enum.

When debugging, I can see that it runs through the following classes (stack):

findEnum():120, EnumResolver (com.fasterxml.jackson.databind.util)
deserialize():79, EnumDeserializer (com.fasterxml.jackson.databind.deser.std)

It looks like they are only interested in the Enum's "keys" (i.e. constants, e.g. "MR" instead of "Mr"). I'm guessing that the @JsonCreator annotation is ignored for some reason.

Any idea how I can fix that issue? Is there a configuration value that might be set anywhere that might cause this behaviour? (I'm working on a big projects; if I know what I need to look for I can search the code base; maybe another developer "misconfigured" something somewhere...) Or might the issue be that Title lives in Clazz? Do I need to throw an @JsonProperty in for good measure? (If so, how exactly?)

We are using jackson-core, -annotations, and -databind 2.4.2.

Update: I tried this as a stand-alone project, with the following code, and it worked flawlessly - which means there must be some sort of setting that prevents the annotation from being taken into account...

ObjectMapper mapper = new ObjectMapper(); // create once, reuse
Clazz value = mapper.readValue(new File("resources/data.json"), Clazz.class);
Christian
  • 6,070
  • 11
  • 53
  • 103

1 Answers1

3

So, it turns out we had the following in our Spring config:

<bean id="mapper" class="com.example.foo.jaxb.links.JsonMapper" />
<!-- ... -->
       <jaxrs:providers>
           <bean id="jsonprovider"
                 class="com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider">
               <constructor-arg ref="mapper" />
               <constructor-arg>
                   <value></value>
               </constructor-arg>
           </bean>
       </jaxrs:providers>

Jsonmapper extends com.fasterxml.jackson.databind.ObjectMapper, and mostly sets Jackson settings on itself in its constructor. The one thing I had missed in my desperation, however, is this bit of code:

// use JAXB annotations (only) 
setAnnotationIntrospector(new JaxbAnnotationIntrospector(
    com.fasterxml.jackson.databind.type.TypeFactory.defaultInstance()));

And it pretty much does what it says on the tin, it seems: it stops Jackson from evaluating the @JsonCreator and @JsonValue annotations. Once the "Jackson annotation suppressor" is gone, all is good.

What I'd like to do is understand how it actually works, so if anyone has any helpful links to docs/how-tos/manuals/books for either the Spring stuff or the suppression of annotations, that would be much appreciated. But in the meantime, this fixes my problem. :)

Christian
  • 6,070
  • 11
  • 53
  • 103