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.
Related
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();
}
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)
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
I have different xml files with different namespaces eg - oa, ns2, p...etc
I want the namespace to be ignored when converting the xml back to java object.
or I want the namespace to be removed from the xml when converting the xml to object.
My sample xml data is
<oa:ApplicationArea>
<oa:Sender>
<oa:LogicalId>Volvo</oa:LogicalId>
<oa:Component>DVLA</oa:Component>
<oa:Task>ReceiveKeeper</oa:Task>
<oa:MessageCode>MS1</oa:MessageCode>
<oa:AuthorizationId>SKARUPAI</oa:AuthorizationId>
<oa:OrganisationLevel>50</oa:OrganisationLevel>
</oa:Sender>
<oa:CreationDateTime>2013-08-31T12:00:00</oa:CreationDateTime>
<oa:BODId>123456789</oa:BODId>
</oa:ApplicationArea>
<p:DataArea>
<oa:Sync confirm="Always">
<oa:SyncCriteria expressionLanguage="XPath">
<oa:SyncExpression action="Add"></oa:SyncExpression>
</oa:SyncCriteria>
</oa:Sync>
</p:DataArea>
Below is the sample of the code which I am using to convert the above xml to java using Xstream.
public static void main(String[] args) throws Exception {
FileReader fileReader = new FileReader("C:/kspace/xstream/src/input.out.xml"); // load our xml file
XStream xstream = new XStream(); // init XStream
// Determine type of message(Eg. 'EM1') and put the corresponding value from hashmap to a String.
// Pass the string to xstream.alias(stringnamewhichwasset, xmlRoot.class)
String interfaceMessageId = "MS1";
String xmlRootTagName = (String) messages.get(interfaceMessageId);
xstream.registerConverter(new XMLDateConverter());
xstream.alias(xmlRootTagName, RootType.class);
xstream.aliasField("ApplicationArea", RootType.class, "applicationArea");
xstream.aliasField("DataArea", RootType.class, "dataArea");
xstream.alias("ApplicationArea", ApplicationArea.class);
xstream.aliasField("Sender", ApplicationArea.class, "sender");
xstream.aliasField("CreationDateTime", ApplicationArea.class, "creationDateTime");
xstream.aliasField("BODId", ApplicationArea.class, "bodId");
xstream.alias("Sender", Sender.class);
xstream.aliasField("LogicalId", Sender.class, "logicalId");
xstream.aliasField("Component", Sender.class, "component");
xstream.aliasField("Task", Sender.class, "task");
xstream.aliasField("MessageCode", Sender.class, "messageCode");
xstream.aliasField("AuthorizationId", Sender.class, "authorizationId");
xstream.aliasField("OrganisationLevel", Sender.class, "organisationLevel");......
......
Is there any way to do it with Xstream at all?
Finally got the solution after trying using StaxDriver.
Here is what I did.
QNameMap qmap = new QNameMap();
qmap.setDefaultNamespace("http://www.somename.com/xyz");
qmap.setDefaultPrefix("");
StaxDriver staxDriver = new StaxDriver(qmap);
XStream xstream = new XStream(staxDriver);
Creating the xstream object like that will ignore the namespace prefixes while parsing.
So far I was successfully able to remove all the namespace prefixes.
I have to marshall a fragment of my root xml object :
Header header = ebicsNoPubKeyDigestsRequest.getHeader();
JAXBElement<org.ebics.h003.EbicsNoPubKeyDigestsRequest.Header> jaxbElement =
new JAXBElement<EbicsNoPubKeyDigestsRequest.Header>(
new QName("header"), EbicsNoPubKeyDigestsRequest.Header.class, header);
byte[] headerXml = JAXBHelper.marshall(jaxbElement, true);
but when I marshall ebicsNoPubKeyDigestsRequest the namespaces are not the same (in header fragment I have : xmlns:ns4="http://www.ebics.org/H003" but in ebicsNoPubKeyDigestsRequest I have xmlns="http://www.ebics.org/H003")
If I marshall the header object directly, without using JAXBElement, I have an No #XmlRootElement error
How I can have the same namespaces?
NB : I already use a NamespacePrefixMapper class :
marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new NamespacePrefixMapper() {
#Override
public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
if (namespaceUri.equals("http://www.ebics.org/H003")) {
return "";
} else if (namespaceUri.equals("http://www.w3.org/2000/09/xmldsig#")) {
return "ds";
} else if (namespaceUri.equals("http://www.ebics.org/S001")) {
return "ns1";
} else if (namespaceUri.equals("http://www.w3.org/2001/XMLSchema-instance")) {
return "ns2";
}
return "";
}
});
EDIT : here the different package-info.java :
#javax.xml.bind.annotation.XmlSchema(namespace = "http://www.ebics.org/H003", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package org.ebics.h003;
#javax.xml.bind.annotation.XmlSchema(namespace = "http://www.ebics.org/S001", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package org.ebics.s001;
#javax.xml.bind.annotation.XmlSchema(namespace = "http://www.w3.org/2000/09/xmldsig#", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package org.w3._2000._09.xmldsig_;
Without seeing your actual XML schema, files and JAXB generated classes (and their annotations) I can only advise you to try an adapt the following example to your scenario.
Given that you have a JAXB generated class like the following:
#XmlRootElement(namespace = "http://test.com")
#XmlType(namespace = "http://test.com")
public static final class Test {
public String data;
public Test() {}
}
which is in the package test and there is a package-info.java file in it like this:
#XmlSchema(elementFormDefault = XmlNsForm.QUALIFIED,
xmlns = #XmlNs(prefix = "", namespaceURI = "http://test.com"))
package test;
import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
the following code:
Test test = new Test();
test.data = "Hello, World!";
JAXBContext context = JAXBContext.newInstance(Test.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(test, System.out);
will print this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<test xmlns="http://test.com">
<data>Hello, World!</data>
</test>
You can probably probably omit a the NamespacePrefixMapper implementation entirely.
Play with omitting particular namespace elements from any of the annotations and see how the output changes.
Blaise Doughan (a Java XML binding expert, probably lurking around somewhere near) has posted some info regarding this issue on his blog, see his post for some more insight.
Based on the input provided by Baptiste in chat I propose the following solution (I think this is the most painless).
Compile your schema files with XJC normally ($ xjc .). This will generate package-java.info files for each of the generated packages. I assume this schema isn't updated everyday, so you're safe to make modifications on the package-info.java files (even though there will be some line comments in those files telling you not to do it—do it anyway). If the schema gets updated and you have to re-compile it run XJC with the -npa switch, which tells it not to generate those package-info.java files automatically, so (ideally) you can't overwrite your hand-crafted files (if you use version control you can/should include these (handmade) files in the repository).
Based on the provided schema files four packages are generated, so I'll include my version of the modified package-info.java files.
#XmlSchema(namespace = "http://www.ebics.org/H000",
xmlns = #XmlNs(prefix = "ns1",
namespaceURI = "http://www.ebics.org/H000"))
package org.ebics.h000;
#XmlSchema(namespace = "http://www.ebics.org/H003",
xmlns = #XmlNs(prefix = "",
namespaceURI = "http://www.ebics.org/H003"))
package org.ebics.h003;
#XmlSchema(namespace = "http://www.ebics.org/S001",
xmlns = #XmlNs(prefix = "ns3",
namespaceURI = "http://www.ebics.org/S001"))
package org.ebics.s001;
#XmlSchema(namespace = "http://www.w3.org/2000/09/xmldsig#",
xmlns = #XmlNs(prefix = "ns2",
namespaceURI = "http://www.w3.org/2000/09/xmldsig#"))
package org.w3._2000._09.xmldsig;
After this you create your JAXBContext like this:
JAXBContext context =
JAXBContext.newInstance("org.ebics.h003:org.ebics.s001:org.w3._2000._09.xmldsig");
(I've noticed that you don't use actually use the h000 package so I've omitted it from the package name list. If it is included, then the marshalled XML's root tag will probably contain its namespace and prefix mapping, even tough it isn't used.)
After this, you unmarshall your input XML and do whatever you want to with the object in memory.
Unmarshaller unmarshaller = context.createUnmarshaller();
EbicsNoPubKeyDigestsRequest ebicsNoPubKeyDigestsRequest =
(EbicsNoPubKeyDigestsRequest) unmarshaller.unmarshal(stream);
Now, if you want to marshall only the header tag which is nested inside the ebicsNoPubKeyDigestsRequest you have to wrap it inside a JAXBElement<...> because the header's type EbicsNoPubKeyDigestsRequest.Header isn't annotated with the #XmlRootElement annotation. You have two (in this case one) way to create this element.
Create an ObjectFactory instance for the corresponding package and use its JAXBElement<T> createT(T t) function. Which wraps its input into a JAXBElement<...>. Unfortunately however, for the header field's type (given your schema files) XJC generates no such method, so you have to do it by hand.
Basically you've almost done it right, but when creating the JAXBElement<...> instead of passing it new QName("header") you have to create a fully-qualifed name, which implies that the namespace is specified too. Passing only the name of the XML tag isn't sufficient, because JAXB won't know this way that this particular header tag is part of the "http://www.ebics.org/H003" namespace. So do it like this:
QName qualifiedName = new QName("http://www.ebics.org/H003", "header");
JAXBElement<EbicsNoPubKeyDigestsRequest.Header> header =
new JAXBElement<EbicsNoPubKeyDigestsRequest.Header>(
qualifiedName, EbicsNoPubKeyDigestsRequest.Header.class, header);
I didn't test if changing only the QName instantiation solves your problem, but maybe it will. However I think it won't and you have to manually manage your prefixes to get a nice and consistent result.