I am calling a WCF .Net service from a Java proxy. The result is an xml string (in a stream) with inline schema. I would like to unmarshall the values in the xml to a Java class using JAXB. The xml string appears as follows:
<NewDataSet>
<xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:Locale="">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="Table">
<xs:complexType>
<xs:sequence>
<xs:element name="CustomerID" type="xs:int" minOccurs="0" />
<xs:element name="CustomerNme" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:schema>
<Table>
<CustomerID>1111</CustomerID>
<CustomerNme>Customer One</CustomerNme>
</Table>
<Table>
<CustomerID>2222</CustomerID>
<CustomerNme>Customer Two</CustomerNme>
</Table>
</NewDataSet>
As the basic structure will remain the same my initial thoughts are to:
- Use the schema to manually create JAXB Java classes using xjc.
- During processing, parse the xml string with embedded schema and throw away the schema and root node NewDataSet.
- Use JAXB to unmarshall the string to a java object.
Is there a better way to do this?
UPDATE
Trying @Doughans approach. Close but no cigar yet. A complication is the the xml comes gzipped. So, code below is where I've got to. I've set the Customer class members to upper case to match the exact xml elements but all I get is null for each instance of the unmarshal operation.
//everything good to here...
JAXBElement<byte[]> byteRet = searchCustResponse.getSearchCustomersResult();
byte[] gzBytes = byteRet.getValue(); //these are gzipped bytes
//decompress...
ByteArrayInputStream bais = new ByteArrayInputStream(gzBytes);
GZIPInputStream gzipis = new GZIPInputStream(bais);
//InputStreamReader isr = new InputStreamReader(gzipis, "UTF-8");
//convert xml to string...
StringWriter writer = new StringWriter();
IOUtils.copy(gzipis, writer, "UTF-8");
String xmlString = writer.toString();
//read the string...
//StreamSource xml = new StreamSource(xmlString);
Reader reader = new StringReader(xmlString);
XMLInputFactory xif = XMLInputFactory.newFactory();
XMLStreamReader xsr = xif.createXMLStreamReader(reader);
List<WPTSCustomer> wptsCustomers = new ArrayList<WPTSCustomer>();
try {
while (xsr.hasNext()) {
int next = xsr.next();
//xsr.nextTag();
if (next == XMLStreamReader.START_ELEMENT) {
if (xsr.getLocalName().equals("Table")) {
JAXBContext jc = JAXBContext.newInstance(WPTSCustomer.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
JAXBElement<WPTSCustomer> jb = unmarshaller.unmarshal(xsr, WPTSCustomer.class);
WPTSCustomer customer = jb.getValue();
System.out.println(customer.getCustomerID()); //prints null
System.out.println(customer.getCustomerNme()); //prints null
wptsCustomers.add(customer);
}
}
}
} finally {
The use of StreamSource did not work for me. Finally I just walked the xml string and that is working up to the point it does the unmarshal which is not populating the object.
UPDATE2
Giving up on JAXB for now. The following code works nicely and I'll just throw the values into my POJO. I did try creating an XSD in JDev from the full xml string returned from the service, then a JAXB class from the XSD. That almost worked except for name conflicts coming from the inline schema. There's probably a way to get around that.
int eventType = xsr.getEventType();
while (xsr.hasNext()) {
eventType = xsr.next();
if (xsr.isStartElement() && xsr.getLocalName().equals("Table")) {
xsr.nextTag();
while (xsr.hasNext()) {
if (xsr.isStartElement()) {
if (xsr.getLocalName().equals("CustomerID")) {
System.out.println("id: " + xsr.getElementText());
}
if (xsr.getLocalName().equals("CustomerNme")) {
System.out.println("name: " + xsr.getElementText());
}
}
xsr.next();
}
}
--