jaxb: exceção de elenco de classe estranha na lista de enum
estou usandojaxb
para gerar classes java a partir de umxsd
Arquivo. oxsd
contém uma definição de um elemento cujo conteúdo é uma lista de constantes definidas no mesmoxsd
como enumeração.
Quando as classes são geradas usando a implementação de referência JAXB do oraclejdk1.7
(v2.2.4-2
) é possível percorrer a lista de enumerações e atribuir a elas as variáveis do mesmo tipo.
Entretanto, quando as classes são geradas usando o oraclejdk1.8
(Construir1.8.0_45-b15
- mais recente a partir da data de publicação) Implementação de referência JAXB (v2.2.8-b130911.1802
) não é mais possível atribuir os elementos da lista à variável do tipo enum.
Qualquer tentativa de atribuir ou iterar usando o loop for avançado termina com umClassCastException
java.lang.ClassCastException: java.lang.String cannot be cast to so.jaxb.enums.generated.GConstNameType
at so.jaxb.enums.domain.TestReader.readTest(TestReader.java:36)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
A lista em si é nos dois casos parametrizada com o tipo de enumeração correto.
Aqui está um código que reproduz o problema descrito acima:
Arquivo XSD
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://www.foo.com/xmlns/test"
targetNamespace="http://www.foo.com/xmlns/test"
attributeFormDefault="unqualified"
elementFormDefault="qualified">
<xs:simpleType name="GConstType">
<xs:list itemType="tns:GConstNameType" />
</xs:simpleType>
<xs:simpleType name="GConstNameType">
<xs:restriction base="xs:string">
<xs:enumeration value="FOO" />
<xs:enumeration value="BAR" />
<xs:enumeration value="BAZ" />
</xs:restriction>
</xs:simpleType>
<xs:complexType name="TestType">
<xs:all>
<xs:element name="const-name-list"
type="tns:GConstType" minOccurs="0" maxOccurs="1" />
</xs:all>
</xs:complexType>
<xs:element name="test" type="tns:TestType" />
</xs:schema>
Arquivo XML de teste
<?xml version="1.0" encoding="UTF-8"?>
<t:test xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:t="http://www.foo.com/xmlns/test"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<t:const-name-list>FOO BAR</t:const-name-list>
</t:test>
Leitor de teste
public class TestReader {
@Test
public void readTest() throws IOException {
try (InputStream xml = TestReader.class
.getResourceAsStream("/so/jaxb/enums/resources/test.xml");
InputStream xsd = TestReader.class
.getResourceAsStream("/so/jaxb/enums/resources/test.xsd")) {
TestType testType = fromXML(TestType.class, xml, xsd);
List<GConstNameType> constNameList = testType.getConstNameList();
for(Object constName : constNameList) {
System.out.println(constName.getClass().getName());
}
for(GConstNameType constName : constNameList) {
System.out.println(constName);
}
}
}
public static <T> T fromXML(Class<T> _class, InputStream xml, InputStream xsd) {
XMLStreamReader xsr = null;
try {
Source xmlSource = new StreamSource(xml);
Source xsdSource = new StreamSource(xsd);
JAXBContext jaxbContext = JAXBContext.newInstance(_class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
Schema schema = SchemaFactory
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(xsdSource);
unmarshaller.setSchema(schema);
xsr = XMLInputFactory.newInstance().createXMLStreamReader(xmlSource);
JAXBElement<T> jaxbElement = unmarshaller.unmarshal(xsr, _class);
return jaxbElement.getValue();
} catch (JAXBException | SAXException | XMLStreamException | FactoryConfigurationError e) {
throw new RuntimeException(e);
} finally {
try {
if(xsr != null) {
xsr.close();
}
} catch(XMLStreamException e) {
throw new RuntimeException(e);
}
}
}
}
Saída jdk1.7
so.jaxb.enums.generated.GConstNameType
so.jaxb.enums.generated.GConstNameType
FOO
BAR
Saída jdk1.8
java.lang.String
java.lang.String
<ClassCastException>
A partir da saída acima, fica claro que elementos do tipojava.lang.String
são contrabandeados para a listaList<GConstNameType>
ou que uma lista deString
s está definido em vez de umGConstNameType
s Lista. De qualquer forma, oString
Os nomes de enumerações do arquivo xml não são mapeados para constantes de enumeração java.
O tempo de execução do java é, nos dois casos, o mesmo, é ojre
dejdk1.8
.
Os comandos usados para geração:
C:\Oracle\Java\jdk1.7\bin\xjc.exe D:\dev\java\Tests\src\so\jaxb\enums\resources\test.xsd -b D:\dev\java\Tests\src\so\jaxb\enums\resources -d D:\dev\java\Tests/src -p so.jaxb.enums.generated -extension
e
C:\Oracle\Java\jdk1.8\bin\xjc.exe D:\dev\java\Tests\src\so\jaxb\enums\resources\test.xsd -b D:\dev\java\Tests\src\so\jaxb\enums\resources -d D:\dev\java\Tests/src -p so.jaxb.enums.generated -extension
Como isso é possível / O que está acontecendo aqui?A definição de esquema mostrada acima não está correta para definir uma enumeração?Como solucionar esse problema sem precisar usar umXmlAdapter
(uma maneira que funcionaria em todas as versões do jdk)?EDITAR
A única diferença de código entre os dois pacotes gerados
Removendo a anotação
@XmlSchemaType(name = "anySimpleType")
processa ojdk1.8
código gerado totalmente funcional.
JAXB
implementação mapeando o enum paraanySimpleType
?