Skip XML nodes while unmarshaling XML with JAXB - java

Is it possible to unmarshal a XML file like this:
<company id="bh23" name="imob">
<store>
<store-info id="2392">
<address>NYC</address>
<name>Imob's NYC 5th</name>
</store>
<products>
<product>
<name>keyboard</keyboard>
<price>2000</price>
</product>
<product>
<name>mouse</keyboard>
<price>1000</price>
</product>
</products>
</store>
<store />
</stores>
into classes like these:
#XmlElementRoot(name = "company")
public class Company {
#XmlAttribute (name = "id")
String id;
#XmlAttribute (name = "name")
String name;
#XmlElement (name = "store")
List<Store>stores;
//all the getters and setters
}
#XmlElementRoot (name = "store")
public class Store {
#XmlAttribute (name = "id")
String id;
#XmlElement (name = "address")
String address;
#XmlElement (name = "name")
String name;
#XmlElementWrapper (name = "products")
#XmlElement (name = "product")
List<Product>products;
//all the getters and setters
}
public class main {
public static void main (String args[]) {
try {
JAXBContext jaxbContext = JAXBContext.newInstance(Company.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Company portfolio = (Company) jaxbUnmarshaller.unmarshal(new File(xmlUrlPath));
System.out.println(portfolio.toString());
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
I'm trying to "skip" or "jump" the node named "store-info" because I don't want to create another class just to keep the store's address and name, since it would be more simple to "append" both address and name to "Store" class.
Of course, when I run the code, the vars "address", "id" and "name" becomes null and only the list of products is correctly unmarshaled.
Is there a way to skip a node, merging their fields into another class? I'm avoiding (for "legal" purposes) the use of MOXy lib and their XPath annotation.

You could create a StAX filtered XMLStreamReader and have JAXB unmarshal that to ignore one or more XML elements.
http://docs.oracle.com/javase/7/docs/api/javax/xml/stream/XMLInputFactory.html#createFilteredReader%28javax.xml.stream.XMLStreamReader,%20javax.xml.stream.StreamFilter%29
Below is a link to a full example I gave in an answer to a similar question:
JAXB filtered parsing

Related

how to wrap non collection property in xml?

I know this question was asked before, but still no response to that.
Indeed, I have this Java Entity:
#Entity
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String email;
private String firstName;
private String lastName;
}
and my goal is to transform/map the class below into this xml format:
<customer>
<id>...</id>
<email>....</email>
<names>
<firstName>...</firstName>
<lastName>...</lastName>
<names>
</customer>
The problem is that I can't use #XmlElementWrapper because it is not applicable on non collection property, and I'm searching for a solution that doesn't require to create a class "names" which will contains firstname and lastname.

unmarshal using jaxb having inner tag

I want to unmarshal my below xml file using JAXB.
<School>
<Student>
<Name> My xyz<Name>
<Hobbies> Playing Cricket <sup>+</sup> Watching TV</Hobbies>
</Student>
</School>
In Above example , There is one school with student which having hobbies.
Whenever i unmarshal above xml using (#XmlPath) i got only "Watching TV" but not "Playing Cricket".
I have tried to unmarshal using "DomHandler" also but not success.
Thanks.
I think your XML should be something like this:
<?xml version="1.0" encoding="UTF-8"?>
<School>
<Student>
<Name>My xyz</Name>
<Hobbies>
<Name>Playing Cricket</Name>
<Hobby>
</Hobby>
<Name>Playing Cricket</Name>
</Hobbies>
</Student>
<!-- OTHER STUDENTS FOLLOW-->
</School>
and then the correspondant java object structure would be something like:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "School")
class School {
#XmlElement(required = true, name = "Student")
Student student;
}
#XmlAccessorType(XmlAccessType.FIELD)
class Hobby {
#XmlElement(required = true, name = "Name")
String name;
}
#XmlAccessorType(XmlAccessType.FIELD)
class Student {
#XmlElement(required = true, name = "Name")
String name;
#XmlElement(required = true, name = "Hobbies")
List<Hobby> hobbies;
}
I took off the getters and the setters for brevity.

JAXB mapping 1 XML Tag to 2 variables

i am trying to use one class to map the response i get from an XML request.
But the xml response differs, depending own some settings. For example in a response i get the tag "owner" which is filled with the ID of the owner object. If i add a setting in my request i will get back the full owner data, like the firstname and lastname. Now i want to map the owner tag to either a String variable or a Class depending on the response.
Example :
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "domain")
public class Response {
#XmlElement
private String name;
#XmlElement(name = "owner")
private String ownerSimple;
#XmlElement(name = "owner")
private Owner ownerComplex;
}
#XmlRootElement(name = "ownerc")
public class OwnerC {
#XmlElement
int id;
#XmlElement
String fname;
#XmlElement
String lname;
}
XML to map :
<response>
<name>Foo</name>
<owner>1234</owner> <!-- in this case it's only a id -->
</response>
<response>
<name>Foo</name>
<owner> <!-- in this case it's the owner class -->
<id>1234</id>
<fname>Jon</fname>
<lname>Doe</lname>
</owner>
</response>
You can use #XmlAnyElement(lax=true) to handle this use case. This annotation allows you to unmarshall any XML to a Java object (DOM Node). In a second step, it is possible to unmarshall the Node to the required Object
Response
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "domain")
public class Response {
#XmlElement
private String name;
#XmlAnyElement(lax=true)
private Object owner;
private String ownerSimple;
#XmlTransient
private Owner ownerComplex;
Owner
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "owner")
public class Owner {
#XmlElement
int id;
#XmlElement
String fname;
#XmlElement
String lname;
Unmarshaller
//Unmarshaller. Step 1 - Decodes Response and set a DOM Node at Owner
//Important. Owner class must not be present in JAXB context, letting next step to decode the object properly.
//Owner variable at Response class is annotated with #XmlTransient
JAXBContext jaxbContext = JAXBContext.newInstance(Response.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Response response = (Response) jaxbUnmarshaller.unmarshal(reader);
//Unmarshaller. Step 2. Convert Node to the suitable Object
//Considering both cases, simple-> String complex -> Owner Object.
String ownerSimple = ((Node)response.getOwner()).getFirstChild().getNodeValue();
if (ownerSimple != null){
response.setOwnerSimple(ownerSimple);
} else {
JAXBContext jaxbContextOwner = JAXBContext.newInstance(Owner.class);
Unmarshaller jaxbUnmarshallerOwner = jaxbContextOwner.createUnmarshaller();
Owner ownerComplex = (Owner) jaxbUnmarshallerOwner.unmarshal((Node)response.getOwner());
response.setOwnerComplex(ownerComplex);
}
//Marshaller to system.out. Your object is well mapped in both cases
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(rx, System.out);

jaxb reference xmlID between xml files

I tried the approach in this post
However I am getting a
>
1 counts of IllegalAnnotationExceptions
XmlIDREF property is referencing a type "java.lang.String" that doesn't have an XmlID property.
this problem is related to the following location:
at private externalReferences.Department
externalReferences.Employee.department
at externalReferences.Employee
at private java.util.List externalReferences.Company.employees
at externalReferences.Company
The two xml Files are the following:
employee.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<company>
<employeeList>
<employee name="Jane Doe" id="1">
<department>1</department>
</employee>
<employee name="John Smith" id="2">
<department>2</department>
</employee>
<employee name="Anne Jones" id="3">
<department>3</department>
</employee>
</employeeList>
</company>
department.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<departmentList>
<departmentList>
<department name="Dev" id="1"/>
<department name="Sales" id="2"/>
<department name="Research" id="3"/>
</departmentList>
</departmentList>
The employee.xml references the department and I want to point to the right department objects when unmarshalling employee.xml.
Classes are as follows:
Company.java
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Company {
#XmlElementWrapper(name = "employeeList")
#XmlElement(name="employee")
private List<Employee> employees;
#XmlElementWrapper(name = "departmentList")
#XmlElement(name="department")
private List<Department> departments;
public Company() {
employees = new ArrayList<Employee>();
departments = new ArrayList<Department>();
}
...
}
Employee.java
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Employee {
#XmlAttribute
#XmlID
private String id;
public String getId() {
return id;
}
#XmlIDREF
private Employee manager;
#XmlJavaTypeAdapter(EmpAdapter.class)
#XmlIDREF
private Department department;
}
Department.java
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Department {
#XmlAttribute
#XmlID
private String id;
...
}
DepartmentList.java
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class DepartmentList {
#XmlElementWrapper(name = "departmentList")
#XmlElement(name="department")
private List<Department> departments;
Then I run the following in Main
JAXBContext jc = JAXBContext.newInstance(DepartmentList.class); Unmarshaller unmarshaller = jc.createUnmarshaller();
DepartmentList depList = (DepartmentList) unmarshaller.unmarshal(new FileReader(DepRef));
EmpAdapter adapter = new EmpAdapter();
for(Department dep : depList.getDepartments()) {
adapter.getDepList().put(dep.getId(), dep);
}
JAXBContext jc2 = JAXBContext.newInstance(Company.class);
Unmarshaller unmarshaller2 = jc2.createUnmarshaller();
unmarshaller2.setAdapter(adapter);
Company company2 = (Company) unmarshaller2.unmarshal(new FileReader(empRef));
I feel that having one XMLIDREF refer to employee id and the other XMLIDREF refer to department id is part of the problem. But that is required since the manager field references other employee objects.
Can someone please help me with this. Thank you
The problem arises from class Company that corresponds to an XML document containing employees and departments. However, you've got two separate documents. Apparently you want one final class containing both lists.
(1) You could define a class EmployeeList for employees only, similar to the one for departments (DepartmentList). This will still let you write an application class Company into which you set the references for both lists.
(2) Change the annotation for Company.departments
#XmlTransient
private List<Department> departments;
marshal like you do now, and set the List with the reference you have from unmarshalling the corresponding XML into the returned object.

Unmarshalling the middle of xml document using jaxb

I am trying to unmarshall the middle elements of a big xml document. Currently using JAXB and Woodstox.
Example of xml middle elements that I need to unmarshall:
<Values>
<Person ID="ABC">
<FirstName>Shawn</FirstName>
<LastName>Mark</LastName>
<Age>3</Age>
</Person>
<Person ID="DEF">
<FirstName>John</FirstName>
<LastName>Durell</LastName>
<Age>4</Age>
</Person>
</Values>
The jaxb classes that I use are:
#XmlRootElement(name = "Values")
#XmlAccessorType(XmlAccessType.FIELD)
public class Attributes
{
#XmlElement(name = "Person")
private ArrayList<Person> persons;
public ArrayList<Person> getPersons()
{
return persons;
}
}
#XmlAccessorType(XmlAccessType.FIELD)
public class Person
{
#XmlAttribute
private String ID;
#XmlElement(name = "FirstName")
private String firstName;
#XmlElement(name = "LastName")
private String lastName;
#XmlElement(name = "Age")
private String age;
}
I am able to unmarshall all values except the ID. Its being shown as null.
Here is the code:
final XMLInputFactory xif = XMLInputFactory.newInstance();
final StreamSource xml = new StreamSource(pathToxmlFile);
XMLStreamReader xsr;
xsr = xif.createXMLStreamReader(xml);
xsr.nextTag();
while (!xsr.getLocalName().equals("Values"))
{
xsr.nextTag();
}
final JAXBContext jc = JAXBContext.newInstance(Attributes.class);
final Unmarshaller unmarshaller = jc.createUnmarshaller();
final JAXBElement<Attributes> jb = unmarshaller.unmarshal(xsr, Attributes.class);
The above code is working only when the <Values> is nested 5-6 levels from the root. If there exists 15 tags before <Values>, this code isn't working.
Also its comparatively very slow when compared to just only using JAXB and unmarshalling all elements, but that would require me to create objects for data which will never be used.
So, my questions are -- Is there anyway to increase the performance?
Why wouldn't it work when its nested deep in the xml?
How to get the ID value from Person attribute?
The following should help:
Why wouldn't it work when its nested deep in the xml?
If by not working you mean throwing an exception like:
Exception in thread "main" javax.xml.stream.XMLStreamException: ParseError at [row,col]:[4,13]
Message: found: CHARACTERS, expected START_ELEMENT or END_ELEMENT
at com.sun.org.apache.xerces.internal.impl.XMLStreamReaderImpl.nextTag(XMLStreamReaderImpl.java:1247)
at blog.stax.middle.UnmarshalDemo.main(UnmarshalDemo.java:15)
The you could change the code that advances the XmlStreamReader to:
while(xsr.hasNext()) {
if(xsr.isStartElement() && xsr.getLocalName().equals("Values")) {
break;
}
xsr.next();
}
Is there anyway to increase the performance?
StAX is a very fast way to parse an XML document. It is probably being used by your JAXB implementation anyways. String comparison can be slow.
Since you are using Woodstox and it interns element names (see: section 6.1 String interning: http://woodstox.codehaus.org/FAQ). You could do identity checks on the strings instead of the equals methods.
if(Boolean.TRUE.equals(xsr.getProperty("org.codehaus.stax2.internNames"))) {
while(xsr.hasNext()) {
if(xsr.isStartElement() && xsr.getLocalName() == "return") {
break;
}
xsr.next();
}
} else {
while(xsr.hasNext()) {
if(xsr.isStartElement() && xsr.getLocalName().equals("return")) {
break;
}
xsr.next();
}
}
How to get the ID value from Person attribute?
By default the XML your JAXB (JSR-222) implementation will be map your ID field to an attribute called id and not ID. You can override this default as follows:
#XmlAttribute(name="ID")
private String ID;

Categories

Resources