Jackson JSON User Group

JSON in Java the Right Way -- Action, Jackson!

Property genereated twice with JacksonJaxbJsonProvider

Hello

I'm using JacksonJaxbJsonProvider in a CXF JAX-RS application as follows:

In the application context:

    <jaxrs:server id="..." address="...">
        <jaxrs:serviceBeans>
            ...
        </jaxrs:serviceBeans>
        <jaxrs:providers>
            <ref bean="jacksonProvider" />
        </jaxrs:providers>
    </jaxrs:server>

    <bean id="jacksonProvider"
        class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider"
    />

In the domain object:

    @XmlElement(name = "instance_name", nillable = true)
    private String instanceName;

When the object is serialized, however, I notice the following:

    { ... "instance_name":null, ... "instanceName":null, ... }

It appears that instanceName came from the default Jackson behavior and instance_name from the @XmlElement annotation.  I know the documentation says that by default the JacksonJaxbJsonProvider uses both the Json and JAXB annotations, with the Json taking precedence.  I did not use any Json annotations here, so why did a property with the same name as the object field get generated?

Is there a way to ensure that only instance_name is generated, hopefully without using a custom ObjectMapper -- which seems like a lot of extra work)?  Is there a Json annotation that should be added to the field?

Thanks in advance

Ray Toal



Tags: JacksonJaxbJsonProvider, jaxrs

Views: 995

Reply to This

Replies to This Discussion

Properties should only be output once, so this is not working as intended. Can you share bit more of the value class definition? Maybe that could suggest what might be going wrong.

I am not familiar with how CXF uses Jackson, but I assume they implement JAX-RS API the usual way.
Thanks for the quick reply. Here is more of the value class:

@XmlRootElement(name = "job_run")
public class JobRunDto implements Transportable {

@XmlElement(nillable = true)
private UUID id;

@XmlJavaTypeAdapter(value = DateTimeXmlAdapter.class)
@XmlElement(name = "start_time")
private DateTime startTime;

@XmlJavaTypeAdapter(value = DateTimeXmlAdapter.class)
@XmlElement(name = "end_time", nillable = true)
private DateTime endTime;

@XmlElement(nillable = true)
private JobRunStatus status;

@XmlElement(name = "last_checkpoint_time", nillable = true)
private DateTime lastCheckpointTime;

@XmlElement(nillable = true)
private String hostname;

@XmlElement(name = "instance_name", nillable = true)
private String instanceName;

// ... MORE PROPERTIES ...

/** Required constructor for JAXB. */
private JobRunDto() {
// empty constructor for JAXB
}

// ... GETTERS ...

// ... NO SETTERS, BUT THERE IS A BUILDER ...

}

Here is a full serialization output:

{"start_time":"2010-02-15T00:00:00.000-08:00","end_time":null,"last_checkpoint_time":null,"expected_checkpoint_frequency":null,"instance_name":null,"job_version":null,"work_amount":null,"error_amount":null,"error_report":null,"parent_job_name":"Test","hostname":null,"jobVersion":null,"environment":null,"instanceName":null,"expectedCheckpointFrequency":null,"workAmount":null,"errorAmount":null,"errorReport":null,"endTime":null,"lastCheckpointTime":null,"parentJobName":"TestJob2","id":"c377714a-4c5d-4cf7-baaf-ded250a4e1b5","startTime":1266220800000,"status":"RUNNING"}

So I get all the properties via reflection, *plus* the properties which have been renamed with @XmlElement. Before I included jackson-xc in the pom, I was getting *only* the reflection-named properties. After adding jackson-xc, I get duplicates, but only for the ones explicitly named with @XmlElement.

I tried something else: I added, in front of @XmlElement("instance_name"), the annotation @JsonProperty("zzz"). Here the Jaxb provider worked partially: I saw "zzz" instead of "instance_name", so the Json annotations do take precendence over the JAXB annotations. However, I *also* got an "instanceName" property as well....

Are you thinking CXF might be doing something strange?



Tatu Saloranta said:
Properties should only be output once, so this is not working as intended. Can you share bit more of the value class definition? Maybe that could suggest what might be going wrong.

I am not familiar with how CXF uses Jackson, but I assume they implement JAX-RS API the usual way.
Part of the last comment got truncated on the right side. The serialization should have looked like this:

{"start_time":"2010-02-15T00:00:00.000-08:00","end_time":null,
"last_checkpoint_time":null,"expected_checkpoint_frequency":null,
"instance_name":null,"job_version":null,"work_amount":null,"
error_amount":null,"error_report":null,"parent_job_name":"TestJob",
"hostname":null,"jobVersion":null,"environment":null,"instanceName":null,
"expectedCheckpointFrequency":null,"workAmount":null,
"errorAmount":null,"errorReport":null,"endTime":null,
"lastCheckpointTime":null,"parentJobName":"TestJob",
"id":"c377714a-4c5d-4cf7-baaf-ded250a4e1b5",
"startTime":1266220800000,"status":"RUNNING"}

(The startTime vs. start_time is pretty interesting....)
I managed to work around the problem but in doing so I believe I uncovered a Jackson JAXB issue.

First, if I annotate only the fields and not the getters, I get the double property problem.

@XmlJavaTypeAdapter(value = DateTimeXmlAdapter.class)
@XmlElement(name = "start_time")
private DateTime startTime;

will give me:

{ ... "start_time":"2010-02-15T00:00:00.000-08:00", ... "startTime":1266220800000 ... }

The reason for this, I believe, is that the first property comes from the field, and the second comes from the (unannotated) getter.

Second, if I move the annotations off of the fields and add them only to the getters, like so:

@XmlJavaTypeAdapter(value = DateTimeXmlAdapter.class)
@XmlElement(name = "start_time")
public DateTime getStartTime() {
return startTime;
}

the I get the following (!!)

{ ... "startTime":"2010-02-15T00:00:00.000-08:00" ... }

This suggests to me that JacksonJaxbJsonProvider understands @XmlJavaTypeAdapter but **not** @XmlElement!

The only way to fix this is to add @JsonProperty, like so:

@XmlJavaTypeAdapter(value = DateTimeXmlAdapter.class)
@XmlElement(name = "start_time")
@JsonProperty(value = "start_time")
public DateTime getStartTime() {
return startTime;
}

which gives {... "start_time":"2010-02-15T00:00:00.000-08:00" ... } and no "startTime". It works but having both annotations is not desirable. (Note that this service is supposed to write both xml and json, so I can't fallback to the plain JacksonJsonProvider).

Any ideas (1) why the getters are being looked at when the fields are annotated, and (2) why @XmlElement's name is being ignored when on a getter?

Thanks!
I don't know, but it does sound like a bug. Obviously each logical property should be written out just once.
JAXB annotations are a pain in the butt to support for multiple reasons; and specifically because JAXB property model is different from Jackson one. @XmElement is supported; but it could be that some flaw somewhere (like, assuming there must be getter and setter even if annotation is used or something like that).
It is also possible that handling of XmlJavaTypeAdapter could be causing pain... so many things that could be wrong, I just don't have much faith in current Jackson's JAXB annotation handling code.

So -- what is needed is a minimal test case; ideally without CXF part. If you can file a Jira issue, that would be great, so I can follow up on this.

RSS

© 2014   Created by Tatu Saloranta.   Powered by

Badges  |  Report an Issue  |  Terms of Service