Open skwirking opened 3 years ago
Ah. I think I may know the issue here.
PropertyNamingStrategy
is only applied to so-called "implicit" names: that is, names derived from getter/setter/field name. However, "explicit" names defined by things like annotations are not renamed by default: they are assumed to be exact names user wants, not subject to automatic renaming.
However, there is a setting:
MapperFeature.ALLOW_EXPLICIT_PROPERTY_RENAMING
enabling of which will change the behavior to apply naming strategy for such cases too.
So you may want to try that setting to see if it helps.
Thank you very much for the explanation and the suggested fix. That does indeed cause the tests that I included in the description to pass.
However, in the real code I'm working with I find there is still a problem when using the custom PropertyNamingStrategy with properties that are annotated with @XmlID
.
I created the following test to reproduce the error:
Invalid Object Id definition for
com.example.jaxbjackson.JaxbToJsonConverterTest$AnXmlElement
: cannot find property with name 'AnXmlAttribute'
Is there a conflict here between processing JaxB annotations and renaming explicit properties? If so, can you suggest how I might resolve it?
package com.example.jaxbjackson;
import static org.junit.jupiter.api.Assertions.assertEquals;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlEnumValue;
import javax.xml.bind.annotation.XmlID;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import org.junit.jupiter.api.Test;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule;
public class JaxbToJsonConverterTest {
@Test
public void usesJaxBAnnotationAndCustomPropertyNamingStrategyTogether() throws Exception {
AnXmlElement xmlelement = new AnXmlElement();
xmlelement.setAnXmlAttribute(AnXmlAttributesEnumValue.VALUE_ONE);
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JaxbAnnotationModule());
mapper.setPropertyNamingStrategy(new AtSymbolPropertyNamingStrategy());
mapper.configure(MapperFeature.ALLOW_EXPLICIT_PROPERTY_RENAMING, true);
String json = mapper.writeValueAsString(xmlelement);
String[] split = json.split(":");
assertEquals("{\"@AnXmlAttribute\"", split[0]);
assertEquals("\"Value One\"}", split[1]);
}
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "")
@XmlRootElement(name = "AnXmlElement")
static class AnXmlElement {
@XmlAttribute(name = "AnXmlAttribute")
protected AnXmlAttributesEnumValue anXmlAttribute;
public AnXmlAttributesEnumValue getAnXmlAttribute() {
return anXmlAttribute;
}
public void setAnXmlAttribute(AnXmlAttributesEnumValue value) {
this.anXmlAttribute = value;
}
}
@XmlType(name = "anXmlAttributesEnumValues")
@XmlEnum
static enum AnXmlAttributesEnumValue {
@XmlEnumValue("Value One")
@XmlID
VALUE_ONE("Value One");
private final String value;
AnXmlAttributesEnumValue(String v) {
value = v;
}
public String value() {
return value;
}
public static AnXmlAttributesEnumValue fromValue(String v) {
for (AnXmlAttributesEnumValue c: AnXmlAttributesEnumValue.values()) {
if (c.value.equals(v)) {
return c;
}
}
throw new IllegalArgumentException(v);
}
}
@SuppressWarnings("serial")
static class AtSymbolPropertyNamingStrategy extends PropertyNamingStrategy {
private String fieldName(AnnotatedMember member, String defaultName) {
XmlAttribute xmlAttributeAnnotation = member.getAllAnnotations().get(XmlAttribute.class);
if (xmlAttributeAnnotation != null) {
return "@" + xmlAttributeAnnotation.name();
}
return defaultName;
}
@Override
public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) {
return fieldName(method, defaultName);
}
@Override
public String nameForField(MapperConfig<?> config, AnnotatedField field, String defaultName) {
return fieldName(field, defaultName);
}
@Override
public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) {
return fieldName(method, defaultName);
}
}
}
@cowtowncoder any suggestion on how to get past this issue with @XmlID
?
@skwirking No. You may try mailing lists; I don't have time to dig into this at this point in time.
I am trying to use the JaxbAnnotationModule when serialising a jaxb object to string. I am trying to achieve two things:
I had hoped to use JaxbAnnotationModule to achieve 1. and a custom PropertyNamingStrategy to achieve 2. They work separately, but when I try to use both together, the custom PropertyNamingStrategy seems to never be invoked.
I don't know if my approach is wrong, or if this is a bug. Any help would be appreciated. Below is a pom.xml and junit 5 test that should reproduce the issue.