Using XmlAttribute for property with non-scalar type prevents serialization as attribute for other properties
Look at the last line of this class. Incorrect using @XmlAttribute for a bean serialization breaks a serialization of other properties. I excepted to see an error or bean value as an attribute value.
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.dataformat.xml.JacksonXmlModule;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
public class Bug2 {
@XmlRootElement(name = "bean")
public static class Bean {
}
@XmlRootElement(name = "root")
public static class Container {
@XmlAttribute(name = "start")
public String start;
@XmlAttribute(name = "bean")
public Bean bean;
@XmlAttribute(name = "end")
public String end;
}
public static void main(String[] args) throws JsonProcessingException {
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.registerModule(new JaxbAnnotationModule());
xmlMapper.registerModule(new JacksonXmlModule());
Container container = new Container();
container.start = "!start";
container.end = "!end";
System.out.println(xmlMapper.writeValueAsString(container)); // print <root xmlns="" start="!start" end="!end"></root>
Bean bean = new Bean();
container.bean = bean;
System.out.println(xmlMapper.writeValueAsString(container)); // print <root xmlns="" start="!start"><bean></bean><end>!end</end></root>
}
}
Hmmh. This is tricky one, since it is difficult if not impossible to find out cases where output can not be done as an attribute. Low-level generator has no idea; and at high-level, annotation introspector also does not know eventual structure to use...
I think the proper course of action would be to ignore 'as-attribute' suggestion when it can not be used (that is, value not output as scalar). But it definitely should not break output.
A compounding problem here is that due to use of streaming XML writer (Stax), it is not possible to mix attribute writes with element writes (attributes MUST be written before any child elements).
It should be possible, as the first step, to at least catch these cases and give proper error message. I think I'll work on that as the first step.
Actually... I am not sure. I assumed a failure occurred, but instead a different, but well-formed output was produced. While not optimal, for some use cases this would be better than straight failure.
If an exception is desired, perhaps there should be a feature to choose that.
I'll have to think about this bit more.
Added failing test; ideally I think would re-order non-scalar's that can not be output as attributes after ones that can. To me seems better than failure, but regardless both approaches would need code to be aware of what can be output as scalar and what not -- something that is not clear at all due to delegation model.
Had another look and found one blocker: at the point where (re)ordering of attributes is done (either XmlBeanSerializerModifier or constructor of XmlBeanSerializerBase, serializers to use have not been resolved so least-bad initial option of checking type of serializer won't work at that point.
Problem with another obvious alternative (consider type of property) is that type "POJO" really is not a type at all but just something for which no other handling is applied. Avoiding attribute for Collections, arrays and Maps would be relatively easy that way, but POJOs are likely the main use case (as in test).
So back to Ye Olde Drawinge Board.