Проблема: При использовании DynamicJAXBContext
сопоставления для перечислений, сгенерированных MOXy (протестировано с 2.7.4, 2.7.5), показывают нежелательное (просто неверное) поведение:
- Значения, ожидаемые в источнике XML, соответствуют
the java.lang.Enum.name()
с, а не литералам, определенным в XSD. Например: учитывая литерал перечисления XSD fooValue
, MOXy ожидает, что FOO_VALUE
. - Даже если
java.lang.Enum.name()
s используются в источнике XML (который уже взломан!), Динамически генерируемыйjava.lang.Enum
константам не хватает @XmlEnumValue
аннотаций. Это приводит к тому, что при маршалинге генерируется недопустимый XML: в предыдущем примере Marshaller
будет писать FOO_VALUE
вместо fooValue
.
Вопрос: Есть лиспособ изменить это поведение к лучшему? Я мог бы жить с проблемой 1, но проблема 2 делает MOXy совершенно непригодным для меня.
Воспроизведение:
JUnit-Test (импорт для краткости исключен) (ошибка):
@Test
public void test_xmlEnumValue() throws Exception {
String resourcesBasePath = "src/test/resources/enums/";
FileInputStream xsdInputStream = new FileInputStream(resourcesBasePath + "EnumSchema.xsd");
DynamicJAXBContext jaxbContext = DynamicJAXBContextFactory.createContextFromXSD(xsdInputStream, null, null, null);
JAXBUnmarshaller unmarshaller = jaxbContext.createUnmarshaller();
File inputFile = new File(resourcesBasePath + "EnumSchemaInstance.xml");
StreamSource xmlInputStreamSource = new StreamSource(inputFile);
JAXBElement<DynamicEntity> dynamicEntity = (JAXBElement<DynamicEntity>) unmarshaller.unmarshal(xmlInputStreamSource);
Enum testEnumValue = (Enum) dynamicEntity.getValue().get("testEnumValue");
assertThat(testEnumValue.name(), IsEqual.equalTo("FOO_VALUE"));
}
EnumSchema.xsd:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://acme.com/test" targetNamespace="http://acme.com/test"
elementFormDefault="qualified" attributeFormDefault="unqualified" version="1.0">
<xs:element name="test" type="tns:TestType" />
<xs:complexType name="TestType">
<xs:sequence>
<xs:element name="testEnumValue" type="tns:TestEnum" />
</xs:sequence>
</xs:complexType>
<xs:simpleType name="TestEnum">
<xs:restriction base="xs:string">
<xs:enumeration value="fooValue" />
<xs:enumeration value="Bar_Value" />
</xs:restriction>
</xs:simpleType>
</xs:schema>
EnumSchemaInstance.xml:
<test xmlns="http://acme.com/test">
<testEnumValue>fooValue</testEnumValue>
</test>
Дальнейшие исследования:
Самым поздним моментом, когда я все еще мог найти аннотации @XmlEnumValue
на EnumTypeInfo
s, является org.eclipse.persistence.jaxb.compiler.MappingsGenerator.buildJAXBEnumTypeConverter(Mapping, EnumTypeInfo)
:
private JAXBEnumTypeConverter buildJAXBEnumTypeConverter(Mapping mapping, EnumTypeInfo enumInfo){
JAXBEnumTypeConverter converter = new JAXBEnumTypeConverter(mapping, enumInfo.getClassName(), false);
List<String> fieldNames = enumInfo.getFieldNames();
List<Object> xmlEnumValues = enumInfo.getXmlEnumValues();
for (int i=0; i< fieldNames.size(); i++) {
converter.addConversionValue(xmlEnumValues.get(i), fieldNames.get(i));
}
return converter;
}
, проблема на данный момент заключается в том, что fieldNames
иxmlEnumValues
оба содержат только одно значение: value
. Это довольно бесполезно, учитывая, что правильные значения будут доступны в этот момент, если вместо них использовать правильно аннотированные JEnumConstant
s в com.sun.codemodel.JDefinedClass.enumConstantsByName
. Поскольку отображение, созданное этим методом, теперь отображает только "value"
на "value"
, пропущенные значения отображаются в более поздний момент времени, здесь в org.eclipse.persistence.jaxb.JAXBEnumTypeConverter.initialize(DatabaseMapping, Session)
:
public void initialize(DatabaseMapping mapping, Session session) {
Iterator<Enum> i = EnumSet.allOf(m_enumClass).iterator();
while (i.hasNext()) {
Enum theEnum = i.next();
if (this.getAttributeToFieldValues().get(theEnum) == null) {
Object existingVal = this.getAttributeToFieldValues().get(theEnum.name());
if (existingVal != null) {
this.getAttributeToFieldValues().remove(theEnum.name());
addConversionValue(existingVal, theEnum);
} else {
// if there's no user defined value, create a default
if (m_usesOrdinalValues) {
addConversionValue(theEnum.ordinal(), theEnum);
} else {
addConversionValue(theEnum.name(), theEnum);
}
}
}
}
super.initialize(mapping, session);
}
Оставляя использование порядкового номераКроме значений, теперь отображение будет использовать java.lang.Enum.name()
s с одной стороны ( -> Проблема 1 ) и само java.lang.Enum
s с другой стороны. Но поскольку в полях (то есть java.lang.Class.getFields()
) java.lang.Enum
s отсутствуют аннотации @XmlEnumValue
, созданный маршаллером XML также будет содержать java.lang.Enum.name()
s ( -> Problem 2 )
Изменение XML в EnumSchemaInstance.xml
на java.lang.Enum.name()
, то есть:
<?xml version="1.0" encoding="UTF-8"?>
<test xmlns="http://acme.com/test">
<testEnumValue>FOO_VALUE</testEnumValue>
</test>
приводит к обнаружению java.lang.Enum
сопоставлением, что дополнительно проверяет проблему1. Теперь, если JAXBElement<DynamicEntity> dynamicEntity
снова маршалируется следующим образом:
JAXBMarshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(JAXBMarshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(dynamicEntity, System.out);
В выводе XML отображается java.lang.Enum.name()
, дальнейшая проверка проблемы 2:
<?xml version="1.0" encoding="UTF-8"?>
<test xmlns="http://acme.com/test">
<testEnumValue>FOO_VALUE</testEnumValue>
</test>
РЕДАКТИРОВАТЬ: Ссылка на EclipseLink Bugzilla: https://bugs.eclipse.org/bugs/show_bug.cgi?id=552902