FasterXML / jackson-module-jaxb-annotations

(DEPRECATED!!!) Moved to: https://github.com/FasterXML/jackson-modules-base/
https://github.com/FasterXML/jackson-modules-base
44 stars 20 forks source link

Json Serialization Inclusion overrides JAXB's XmlElement's "required" property #68

Closed miaaaooow closed 6 years ago

miaaaooow commented 6 years ago

I am using the following configuration to support both Jackson and JAXB annotations and serialize objects to XML.

    JacksonXmlModule jacksonXmlModule = new JacksonXmlModule();
    jacksonXmlModule.addSerializer(float.class, new CustomFormattedSerializer());

    XmlMapper xmlMapper = new XmlMapper();
    xmlMapper.registerModule(jacksonXmlModule);

    XmlJaxbAnnotationIntrospector xmlJaxbAnnotationIntrospector =
            new XmlJaxbAnnotationIntrospector(xmlMapper.getTypeFactory());
    xmlMapper.setAnnotationIntrospector(xmlJaxbAnnotationIntrospector);

    xmlMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

But, POJO's fields that are annotated with JAXB XmlEmelemt's required property, get ignored, because the flag is overridden by the JsonInclude.Include.NON_NULL serialization strategy (null elements are ignored and empty tag is not added).

@XmlElement(name = "some-value", required = true) 
protected String someValue;

Is there a way to keep this strategy, but respect JAXB's required flag and to write an empty element every time there is no value for it?

miaaaooow commented 6 years ago

I can see that "required" is basically ignored during marshalling. https://javaee.github.io/jaxb-v2/doc/user-guide/release-documentation.html#marshalling-element-default-values-and-marshalling Does anyone have any comment on the best implementation of the above? Additional serializers? Mixins? What would you recommend?

cowtowncoder commented 6 years ago

@miaaaooow required has no effect on serialization (marshalling) since it's not clear to me why it should. In most serialization/binding systems, this indicator means that a value is required to be found from within content read; and does not affect what is written. Do you have a link to something that formally explains expectations from POJOs to XML in JAXB? Explanation on javadoc, for example:

https://docs.oracle.com/javase/7/docs/api/javax/xml/bind/annotation/XmlElement.html#required()

focuses simply on XML Schema generation and not actual processing.

Now, what JAXB annotation introspector does look for is nillable. Since JAXB rules are somewhat confusing (and users seem to disagree on intended semantics anyway, based on bug reports), I don't know if rules make sense or not. But... XmlElementWrapper.nillable and XmlElement.nillable both force inclusion if set to true. So perhaps you want to use that. Or just explicit Jackson annotation (@JsonInclude(Include.ALWAYS)), either directly or via mix-in.

Another option JaxbAnnotationIntrospector exposes is method setNonNillableInclusion(Include inclusion)). If you do not set nillable, this value (defaulting to null, i.e. "no setting") is returned. It takes effect on all properties, as the baseline.

miaaaooow commented 6 years ago

I would not go for Include.ALWAYS just because of performance/verbosity avoidance reasons. Anyway, what worked for me was adding one more custom JsonSerializer. For the specific required property, I just set

public void serialize(ObjectType obj, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
    gen.writeStartObject();
    // write other elements
    String someValue = obj.getSomeValue();
    if (someValue == null) {
        someValue = "";
    }
    gen.writeStringField("some-value", someValue);
    gen.writeEndObject();
}

This did the job. Thanks a lot for your cooperation.

cowtowncoder commented 6 years ago

@miaaaooow Ok. Sounds good; thank you for the update.

One more suggestion that might be useful in future: you can sub-class JaxbAnnotationIntrospector , override method findPropertyInclusion(). This is ultimate what determines settings on per-property basis.

miaaaooow commented 6 years ago

This is actually a cleaner solution, although a bit hackish probably- it is indeed very local, and does not require rewriting of the serialization of an element with many properties (which can be more error-prone). Thanks!