The code below works correctly to unmarshal XML from the stream one object at a time.
But when I un-comment the unmarshaller.setSchema(schema)
line the program throws an exception:
[org.xml.sax.SAXParseException: cvc-elt.1: Cannot find the declaration of element 'Subscriber'.]
I have already validated the XML using the javax.xml.validation.Validator
class, but my goal is to validate and unmarshal at the same time, one element at a time.
This is my current code:
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = sf.newSchema(new File("/Path to xsd"));
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
XMLStreamReader streamReader = inputFactory.createXMLStreamReader(new FileReader("/Path to xml"));
JAXBContext jaxbContext = JAXBContext.newInstance(SubscriberType.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
//unmarshaller.setSchema(schema);
streamReader.nextTag();
streamReader.require(XMLStreamConstants.START_ELEMENT, null, "Subscribers");
streamReader.nextTag();
while (streamReader.getEventType() == XMLStreamConstants.START_ELEMENT) {
JAXBElement<SubscriberType> pt = unmarshaller.unmarshal(streamReader, SubscriberType.class);
//do something with the unmarshalled object pt...store to db ect.
if (streamReader.getEventType() == XMLStreamConstants.CHARACTERS) {
streamReader.next();
}
}
Excerpt of my schema subscriber.xsd:
<?xml version="1.0" encoding="UTF-8" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
elementFormDefault="unqualified"
attributeFormDefault="unqualified">
<xsd:element name="Subscribers" type="SubscriberType" />
<xsd:complexType name="SubscriberType">
<xsd:sequence>
<xsd:element name="Subscriber"
type="SubscriberInformation"
minOccurs="1"
maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
Try it with the schema like this:
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="unqualified" attributeFormDefault="unqualified">
<xsd:element name="Subscribers" type="SubscriberType"/>
<xsd:element name="Subscriber" type="SubscriberInformation" />
<xsd:complexType name="SubscriberType">
<xsd:sequence>
<xsd:element ref="Subscriber" minOccurs="1" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
What I believe happens with your schema is this: the JAXB context knows the class for SubscriberType
and SubscriberInformation
. If you were to give it an XML document with a <Subscribers>
root element, it knows that it must unmarshal to the class for SubscriberType
. If you gave it an XML document with a <Subscriber>
root element however, it normally wouldn't find this element definition in the ObjectFactory
class generated by XJC. But since you've used the unmarshal
method that takes a second argument, namely the class you're expecting, you've told the unmarshaller that it should interpret its input as a SubscriberType
. The result will be an empty SubscriberType
instance.
Now, since you're iterating over the <Subscriber>
elements one by one (at least that's what I gather you're meaning to do), for the unmarshaller it seems as if it is receiving XML documents with that as root element. It won't complain about not finding that definition since you've taken the task of figuring out the type away with the class argument. But the moment you attach a schema for validation, things break down. The validator doesn't know you're within a <Subscribers>
element. It's expecting a full XML document. So it goes to look for an element declaration for <Subscriber>
but comes up empty, since that element is only defined within a complex type. It's not a global element defintion (i.e. one under the schema root).
So, two things to do here. One is to define element <Subscriber>
as shown above, then reference it in your complex type(s). The other is to change your unmarshal call to unmarshal(streamReader, SubscriberInformation.class)
to get the correct type of object back. Also look out for infinite loops or incorrect unmarshalling, since your call to streamReader.next()
is in a condition and might not fire.
Writing schemas with JAXB in mind requires a certain style. In general, it's best to define elements globally, then reference them. Only define an element locally within a complex type if it absolutely must remain encapsulated there.
Sorry for the long-winded answer, I'm not very well awake :)