Unmarshall XML String using Java - java

Hi I am writing a unmarshalling script for an XML response string using java. I have mentioned the xml response, unmarshalling code and the error i received.
Please help in fixing the issue and also advise me on the problem.
JAXBContext jaxbContext = JAXBContext.newInstance(Customer.class);
javax.xml.bind.Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Customer customer = (Customer) jaxbUnmarshaller.unmarshal(new StreamSource(new StringReader(response.toString() ) ) );
System.out.println(customer.getNAME());
Exception in thread "main" javax.xml.bind.UnmarshalException: unexpected element (uri:"", local:"response"). Expected elements are <{}customer>
<?xml version="1.0" encoding="UTF-8"?>
<response>
<control>
<status>success</status>
<senderid>XXXX</senderid>
<controlid>ControlIdHere</controlid>
<uniqueid>false</uniqueid>
<dtdversion>3.0</dtdversion>
</control>
<operation>
<authentication>
<status>XXXX</status>
<userid>XXXX</userid>
<companyid>XXXXXX</companyid>
<sessiontimestamp>2014-08-12T03:49:00-07:00</sessiontimestamp>
</authentication>
<result>
<status>success</status>
<function>readByQuery</function>
<controlid>testControlId</controlid>
<data listtype="customer" count="26" totalcount="26" numremaining="0">
<customer>
<RECORDNO>15</RECORDNO>
<CUSTOMERID>RIC001</CUSTOMERID>
<NAME>XYZ</NAME>
<ENTITY>CRIC001</ENTITY>
<PARENTKEY></PARENTKEY>
<PARENTID></PARENTID>
<PARENTNAME></PARENTNAME>
</customer>
<customer>
<RECORDNO>15</RECORDNO>
<CUSTOMERID>RIC001</CUSTOMERID>
<NAME>BBB</NAME>
<ENTITY>CRIC001</ENTITY>
<PARENTKEY></PARENTKEY>
<PARENTID></PARENTID>
<PARENTNAME></PARENTNAME>
</customer>
</data>
</result>
</operation>

The problem is that you are telling the unmarshaller that you want a Customer object and will give a XML string representing a Customer, but you are passing it a XML string that represents a Response object. If you have a Response class, use it to create the JAXBContext instance. If not, get the string representing the Customer object in the response
<customer>
<name>ABC</name>
<country>India<country>
</customer>
and use with the unmarshaller.
== Update ==
Assuming you do not have a Response or a Data class, you can use code similar to the following;
XMLInputFactory xif = XMLInputFactory.newInstance();
StreamSource xml = new StreamSource(new StringReader(response.toString()));
XMLStreamReader xsr = xif.createXMLStreamReader(xml);
// Advance to the "Customer" elements
while (xsr.hasNext()) {
if (xsr.isStartElement() && "customer".equals(xsr.getLocalName())) {
// Unmarshal to Customer
JAXBContext jc = JAXBContext.newInstance(Customer.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Customer customer = unmarshaller.unmarshal(xsr, Customer.class).getValue();
customers.add();
}
xsr.next();
}

You're currently trying to turn the XML, which is opened with a <response> tag, into the Customer object.
You need to provide the element specifically to the JAXBUnmarshaller for this to work. For example:
<customer>
<name>ABC</name>
<country>India<country>
</customer>

The XML string is not valid XML (closing tag missing), but I assume that is a mistake when posting the question?
It looks like JAXB isn't expecting the <response> root element when unmarshalling to a Customer object. What does the Customer class look like?

See this question how to walk through the XML until you reached the customer element. From there you can unmarshall the XML :
How to get a particular element through JAXB xml parsing?

Java Class must be decalared with
#XMLRootElement
class Response

Related

Adding new element to XML file using JAXB marshal is adding namespace to it

I have an existing xml file as shown below
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Person xmlns="http://Person">
<name first="abc1",last="xyz1"/>
<name first="abc2",last="xyz2"/>
</Person>
I need to add name element to it dynamically. I have implemented this as described below
1) Unmarshal the xml file
InputStream inputStream = new FileInputStream(xmlFile);
JAXBContext jaxbContext = JAXBContext.newInstance(Person.class);
Unmarshaller u = jaxbContext.createUnmarshaller();
JAXBElement<Person> root = (JAXBElement<Person>) u.unmarshal(xmlFile);
Person personObj = root.getValue();
2) Add the name element
List<JAXBElement> nameList = personObj.getNameElement();
Name name = new Name ();
name.setFirst("abc3");
name.setLast("xyz3");
Name name1 = new Name ();
name1.setFirst("abc4");
name1.setLast("xyz4");
JAXBElement<Name> nameEle = new JAXBElement<Name>(new QName("Name"), Name.class, name);
JAXBElement<Name> nameEle1 = new JAXBElement<Name>(new QName("Name"), Name.class, name1);
nameList.add(nameEle);
nameList.add(nameEle1);
3) marshal the file
JAXBContext jaxbContext = JAXBContext.newInstance(Person.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(personObj, newXMLFile);
Now marshalled xml is showing namespace to newly added elements. I want to know if there is a way to remove this namespace
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Person xmlns="http://Person">
<name first="abc1",last="xyz1"/>
<name first="abc2",last="xyz2"/>
<name xmlns:ns2="http://Person" xmlns="" first="abc3",last="xyz3"/>
<name xmlns:ns2="http://Person" xmlns="" first="abc4",last="xyz4"/>
</Person>
I want to keep the namespace at root element (Person) because the original xml has the namespace for root element

How to make XStream parse partial input from StAX

I am new to Stax and XStream. I am trying to unmarshall some common elements from huge XML stream (there might be between 1.5 million and 2.5 million elements to unmarshal)
I have tried to Stax to parse the stream to get to an element of interest and then call xStream to unMarshall the XML up to the EndElement.
XMLStreamReader reader = xmlInputFactory.createXMLStreamReader(fis);
while (reader.hasNext()) {
if (reader.isStartElement() && reader.getLocalName().toLowerCase().equals("person")) {
break;
}
reader.next();
}
StaxDriver sd = new StaxDriver();
AbstractPullReader rd = sd.createStaxReader(reader);
XStream xstream = new XStream(sd);
xstream.registerConverter(new PersonConverter());
Person p = (Person) xstream.unmarshal(rd);
I create a test input
<Persons>
<Person>
<name>A</name>
</Person>
<Person>
<name>B</name>
</Person>
<Person>
<name>C</name>
</Person>
</Persons>
The problem with this, is that first my converter is not called. Second, I get a CannotResolveClassException for the element "name" in Person and XStream doesn't create my Person object.
What did I miss in my code?
When you instantiate an AbstractPullReader it will read the first open-element event from the stream, establishing the "root" element. Because you've already read the first Person event it will advance to the next one (name), which it doesn't know how to unmarshal.
You'll have to do two things to make your example work:
First, alias the element name Person to your java class
xstream.alias("Person", Person.class);
Second, only advance the SAX cursor up to the element before the one you want to read:
while (reader.hasNext()) {
if (reader.isStartElement() && reader.getLocalName().equals("Persons")) {
break;
}
reader.next();
}

Remove xsi:type information from xml/json JAXB?

I am using JAXB to convert my domain-model to XML and JSON representations.
I have Student pojo to convert to XMl/JSON. It has an content property which can be of any data type.
Schema definition for it:
<xs:element name="content" type="xs:anyType" />
Thus the java file generated has Object type for content.
Student.java:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"content"
})
#XmlRootElement(name = "student")
public class Student
extends People
{
................
#XmlElement(required = true)
protected Object content;
}
I marshall using the following code:
Marshall:
Map<String, Object> properties = new HashMap<String, Object>(1);
properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, "name-binding.xml");
this.ctx = JAXBContext.newInstance("packagename",
packagename.ObjectFactory.class.getClassLoader(), properties);
Marshaller marshaller = ctx.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(MarshallerProperties.MEDIA_TYPE,media-type);
marshaller.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT,true);
marshaller.setProperty(MarshallerProperties.JSON_REDUCE_ANY_ARRAYS, true);
StringWriter sw = new StringWriter();
marshaller.marshal(object, sw);
XML:
<student>
<name>Jack n Jones</name>
<content xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:string">Sid</content>
</student>
xmlns:xsi and xsi:type="xsd:string"> are coming appended in the content element. I don't want this type information in my XML.
Similarly for JSON it adds the type info:
JSON:
{
"name" : "Jack n Jones",
"content" : {
"type" : "string",
"value" : "Sid"
}
}
How can I remove the type information and generate XML/JSON according to it's type at run time. So whatever type is content it get's converted to the type without type information
For example if content is String then XML:
<student>
<name>Jack n Jones</name>
<content>Sid</content>
</student>
Passing an java.lang.Object parameter in and JAXB annotated pojo and having no additionally generated meta information after marshalling is not possible. Since the Object is "unknown" type, it needs to be detected and converted during marshalling process, and the metadata will always be generated by the default marshaller. From this point on, you have three options:
White your custom marshaller or adapter (there are plenty
examples in WEB)
Use String instead of Object (fast and clean
solution)
If you really must use something generic, use
"Element" (https://jaxb.java.net/nonav/2.2.4/docs/api/javax/xml/bind/annotation/XmlAnyElement.html)

Unmarshalling if stream contains collection?

I have two classes at the moment. Customer and CustomerItem, each Customer can contain multiple CustomerItems:
#XmlRootElement(name = "Customer")
#XmlAccessorType(XmlAccessType.FIELD)
public class Customer implements Serializable {
private final String customerNumber;
private final String customerName;
#XmlElementWrapper(name = "customerItems")
#XmlElement(name = "CustomerItem")
private List<CustomerItem> customerItems;
...
Via REST we can get a List<Customer>, which will result in an XML looking like that:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<collection>
<Customer>
// normal values
<customerItems>
<customerItem>
// normal values
</customerItem>
</customerItems>
</Customer>
<Customer>
...
</Customer>
<Customer>
...
</Customer>
</collection>
Now if I want to unmarshal the response I get an error:
javax.xml.bind.UnmarshalException: unexpected element (uri:"",
local:"collection"). Expected elements are
<{}Customer>,<{}CustomerItem>
private List<Customer> getCustomersFromResponse(HttpResponse response)
throws JAXBException, IllegalStateException, IOException {
JAXBContext jaxbContext = JAXBContext.newInstance(Customer.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
InputStream content = response.getEntity().getContent();
InputStreamReader reader = new InputStreamReader(content);
java.util.List<Customer> unmarshal = (List<Customer>) jaxbUnmarshaller.unmarshal(reader);
return unmarshal;
}
Now I know why it's not working, obviously Jaxb expects Customer to be the root element, but now find a collection (which seems to be a default value when a List gets returned?).
A workaround would be to create a new Customers class which contains a list of customer and make it the root element, but actually I wouldn't want a new class.
There must be a way to tell jaxb that I have a list of the classes I want to get and that he should look inside the collection tag or something like that?!
I see here two ways.
1) Create special wrapper class with #XmlRootElement(name = "collection") and set it against unmarshaller.
2) Another way - split input xml into smaller one using simple SAX parser implementation and then parse each fragment separately with JAXB (see: Split 1GB Xml file using Java).
I don't think that you can simply tell JAXB: parse me this xml as set of elements of type XXX.

Adding namespaces to the AXIOMXPath

XML :
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<wmHotelAvailResponse xmlns="http://host.com/subPath">
<OTA_HotelAvailRS Version="1.001">
</OTA_HotelAvailRS>
</wmHotelAvailResponse>
</soap:Body>
</soap:Envelope>
Code :
String xpathString = "/soap:Envelope/soap:Body/wmHotelAvailResponse/OTA_HotelAvailRS";
AXIOMXPath xpathExpression = new AXIOMXPath(xpathString);
xpathExpression.addNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
xpathExpression.addNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
xpathExpression.addNamespace("xsd", "http://www.w3.org/2001/XMLSchema");
OMElement rsMsg = (OMElement)xpathExpression.selectSingleNode(documentElement);
String version = rsMsg.getAttribute(new QName("Version")).getAttributeValue();
Question :
This is working perfectly when the xmlns="http://host.com/subPath" part is deleted. I wanna know how can I add xmlns="http://host.com/subPath" part to the xpathExpression to make the above work
I tried below but didn't work.
xpathExpression.addNamespace("", "http://host.com/subPath");
Solution:
.1. Add this code:
xpathExpression.addNamespace("x", "http://host.com/subPath");
.2. Change:
String xpathString = "/soap:Envelope/soap:Body/wmHotelAvailResponse/OTA_HotelAvailRS";
to:
String xpathString = "/soap:Envelope/soap:Body/x:wmHotelAvailResponse/x:OTA_HotelAvailRS";
Explanation:
Xpath always treats any unprefixed element name as belonging to "no namespace".
Therefore when evaluating the XPath expression:
/soap:Envelope/soap:Body/wmHotelAvailResponse/OTA_HotelAvailRS
the Evaluator tries to find a wmHotelAvailResponse element that is in "no namespace" and fails because the only wmHotelAvailResponse element in the document belongs to the "http://host.com/subPath" namespace.

Categories

Resources