unmarshal using jaxb having inner tag - java

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.

Related

Multiple namespaces with JAXB

I am working on a XML file with multiple naespaces and I am trying ot unmarshal it.
I have looked into some questions on stack overflow previously but have not yet met with the solution.
The XML file:
<?xml version="1.0" encoding="UTF-8"?>
<Registry xmlns="http://www.registar.com" xmlns:ms="http://www.registar.com/ScoreVariant">
<Student>
<FirstName>RP</FirstName>
<Value>
<ms:String>Pass</ms:String>
</Value>
</Student>
<Student>
<FirstName>SK</FirstName>
<Value>
<ms:Int>100</ms:Int>
</Value>
</Student>
</Registry>
The Registry, Student class
#Data
#XmlRootElement(name="Registry")
#XmlAccessorType(XmlAccessType.FIELD)
public class Registry {
#XmlElement
private List<Student> Student;
}
Student:
#Data
#XmlAccessorType(XmlAccessType.FIELD)
//#XmlType(namespace = "http://www.registar.com/Grad")
public class Student {
#XmlElement
private String FirstName;
private Value value; // suggestions to achieve this with different namespaces
}
I have a package-info.java file
#XmlSchema(
namespace="http://www.registar.com",
elementFormDefault=XmlNsForm.QUALIFIED,
xmlns={
#XmlNs(prefix = "", namespaceURI = "http://www.registar.com"),
#XmlNs(prefix = "ms",namespaceURI = "http://www.registar.com/Grad")
}
)
#XmlAccessorType(XmlAccessType.FIELD)
import javax.xml.bind.annotation.*;
And i try to print out the unmarshalled entries. However the different namespaces are not recognised.
The output shows an error..
unexpected element (URI: "http://www.registar.com", local: "Registry"). Expected elements are <{} Registry>
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:744)
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:262)
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:257)
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportUnexpectedChildElement(Loader.java:124)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext$DefaultRootLoader.childElement(UnmarshallingContext.java:1149)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext._startElement(UnmarshallingContext.java:574)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.startElement(UnmarshallingContext.java:556)
at com.sun.xml.bind.v2.runtime.unmarshaller.SAXConnector.startElement(SAXConnector.java:168)
at java.xml/com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.startElement(AbstractSAXParser.java:518)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.scanStartElement(XMLNSDocumentScannerImpl.java:374)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl$NSContentDriver.scanRootElementHook(XMLNSDocumentScannerImpl.java:613)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:3078)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$PrologDriver.next(XMLDocumentScannerImpl.java:836)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:605)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:112)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:541)
at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:888)
at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:824)
at java.xml/com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141)
at java.xml/com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1224)
at java.xml/com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:635)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:258)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:229)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:170)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:209)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:194)
at Mainparser.main(Mainparser.java:20)
Could you please suggest or provide some guidance here?
Update: As per the suggestion, I passed the prefix of ns0 to the default namespace and made the changes. I do not get any error, but the output is not as expected
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Registry>
<Student>
<FirstName>RP</FirstName>
<Value/>
</Student>
<Student>
<FirstName>SK</FirstName>
<Value/>
</Student>
</Registry>
UPDATE2:
The xmlns:ms has the different scorevariants( it could be string or int or double).. I am not able to extract this information as well.. There needs to be a Value class, but I do not seem to find a way to extract the information
Update 3:
The value class was written as per the suggestion.
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class Value {
#XmlElements( value = {
#XmlElement(name="ms:String",type = String.class),
#XmlElement(name="ms:Int",type = Integer.class)
})
private Object value;
}
The output is as below:
<Registry xmlns:ms="http://www.registar.com/ScoreVariant" xmlns="http://www.registar.com">
<Student>
<FirstName>RP</FirstName>
<Value/>
</Student>
<Student>
<FirstName>SK</FirstName>
<Value/>
</Student>
</Registry>
Rest of the code is the same.. Please suggest if there is any dependency that I am missing on..
You would be able to achieve this by doing the following:
Your package-info.java
#XmlSchema(
namespace = "http://www.registar.com",
elementFormDefault = XmlNsForm.QUALIFIED,
xmlns = {
#XmlNs(prefix = "", namespaceURI = "http://www.registar.com"),
#XmlNs(prefix = "", namespaceURI = "http://www.registar.com/Grad"),
#XmlNs(prefix = "ms", namespaceURI = "http://www.registar.com/Value")
}
)
#XmlAccessorType(XmlAccessType.FIELD)
Your Value class, which you did not provide here, so I made an assumption of how it would look like:
#XmlRootElement(name = "Value", namespace = "http://www.registar.com/Value")
#XmlAccessorType(XmlAccessType.FIELD)
public class Value {
#XmlElement(name = "String", namespace = "http://www.registar.com/Value")
private String string;
#XmlElement(name = "Int", namespace = "http://www.registar.com/Value")
private Integer number;
}
Once you do the Marshalling your XML will look like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Registry xmlns="http://www.registar.com" xmlns:ms="http://www.registar.com/Value"
xmlns:ns3="http://www.registar.com/Grad">
<Student>
<FirstName>RP</FirstName>
<Value>
<ms:String>Pass</ms:String>
</Value>
</Student>
<Student>
<FirstName>SK</FirstName>
<Value>
<ms:Int>100</ms:Int>
</Value>
</Student>
</Registry>
Here the namespace is registered and configured to have a prefix, subsequently we tell the fields in the Value class which namespace they're using.
My experience of the actual specs of SOAP are not sufficient to tell you whether this would be a valid SOAP. My testing however allowed for both marshalling and unmarshalling if this XML file.
I believe this is something you are looking for:
XML:
<?xml version="1.0" encoding="UTF-8"?>
<Registry xmlns="http://www.registar.com" xmlns:ms="http://www.registar.com/ScoreVariant">
<Student>
<FirstName>RP</FirstName>
<Value>
<ms:String>Pass</ms:String>
</Value>
</Student>
<Student>
<FirstName>SK</FirstName>
<Value>
<ms:Int>100</ms:Int>
</Value>
</Student>
</Registry>
Registry:
#XmlRootElement(name = "Registry")
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class Registry {
#XmlElement(name = "Student")
List<Student> Student;
}
Student:
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class Student {
#XmlElement(name="FirstName")
private String FirstName;
private Value Value;
}
Value:
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class Value {
#XmlElements({
#XmlElement(name = "ms:String", type = String.class),
#XmlElement(name = "ms:Int", type = Integer.class)
})
private Object value;
}
package-info.java:
#XmlSchema(
elementFormDefault = XmlNsForm.QUALIFIED,
namespace = "http://www.registar.com",
xmlns = {#XmlNs(prefix = "", namespaceURI = "http://www.registar.com"),
#XmlNs(prefix = "ms", namespaceURI = "http://www.registar.com/ScoreVariant")})
package stackover;
import jakarta.xml.bind.annotation.XmlNs;
import jakarta.xml.bind.annotation.XmlNsForm;
import jakarta.xml.bind.annotation.XmlSchema;
Main:
public class Main {
public static void main(String[] args) throws JAXBException, XMLStreamException {
final InputStream inputStream = Main.class.getClassLoader().getResourceAsStream("sample.xml");
final XMLStreamReader xmlStreamReader = XMLInputFactory.newInstance().createXMLStreamReader(inputStream);
final Unmarshaller unmarshaller = JAXBContext.newInstance(Registry.class).createUnmarshaller();
final Registry registry = unmarshaller.unmarshal(xmlStreamReader, Registry.class).getValue();
System.out.println(registry.toString());
Marshaller marshaller = JAXBContext.newInstance(Registry.class).createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(registry, System.out);
}
}
package-info.java
#XmlSchema(
namespace="http://www.registar.com",
xmlns={
#XmlNs(prefix = "",namespaceURI = "http://www.registar.com"),
#XmlNs(prefix = "ms",namespaceURI = "http://www.registar.com/ScoreVariant")
},
elementFormDefault=XmlNsForm.QUALIFIED
)package code;
//#XmlAccessorType(XmlAccessType.FIELD)
import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
Registry(Student=[Student(FirstName=RP, Value=Value(value=Pass)), Student(FirstName=SK, Value=Value(value=100))])
<Registry xmlns="http://www.registar.com" xmlns:ms="http://www.registar.com/ScoreVariant">
<Student>
<FirstName>RP</FirstName>
<Value>
<ms:String>Pass</ms:String>
</Value>
</Student>
<Student>
<FirstName>SK</FirstName>
<Value>
<ms:Int>100</ms:Int>
</Value>
</Student>
</Registry>

JAXB Generate xmlns:xsi declaration in a nested node

I am looking to control where JAXB generates the xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" declaration when marshalling to XML. I've seen solutions like this to add it to the root element using the JAXB_SCHEMA_LOCATION property, however I don't want it on the root node, I want it somewhere in between. Here's what I've got:
#XmlRootElement(name = "RootNode")
#XmlAccessorType(XmlAccessType.NONE)
public class RootNode {
#XmlElement(name = "IntermediateNode")
private IntermediateNode intermediateNode;
//getter & setter
}
#XmlAccessorType(XmlAccessType.NONE)
public class IntermediateNode {
#XmlElement(name = "MyEntity")
private MyEntity myEntity;
//getter and setter
}
#XmlAccessorType(XmlAccessType.NONE)
public class MyEntity {
#XmlElement(name = "Name")
private String name;
#XmlElement(name = "Title", nillable = true)
private String title;
//getters and setters
}
Serialize like:
MyEntity myEntity = new MyEntity();
myEntity.setName("George");
myEntity.setTitle(null);
IntermediateNode intNode = new IntermediateNode();
intNode.setMyEntity(myEntity);
RootNode rootNode = new RootNode();
rootNode.setIntermediateNode(intNode);
JAXBContext context = JAXBContext.newInstance(RootNode.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(rootNode, System.out);
Produces XML like:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RootNode>
<IntermediateNode>
<MyEntity>
<Name>George</Name>
<Title xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
</MyEntity>
</IntermediateNode>
</RootNode>
But what I want is:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RootNode>
<IntermediateNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<MyEntity>
<Name>George</Name>
<Title xsi:nil="true"/>
</MyEntity>
</IntermediateNode>
</RootNode>
I even tried moving my IntermediateNode and MyEntity classes into their own package with a package-info.java like this, but that just rolled the xmlns:xsi up to the root element.
#javax.xml.bind.annotation.XmlSchema(
xmlns = {
#javax.xml.bind.annotation.XmlNs(prefix = "xsi", namespaceURI = "http://www.w3.org/2001/XMLSchema-instance")
},
elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package com.example.intermediate;
Is it possible to get what I want?

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.

Serialize Java List to XML using Jackson XML mapper

Hi I need to create an XML from JAVA using Jackson-dataformat XMLMapper.
The XML should be like
<Customer>
<id>1</id>
<name>Mighty Pulpo</name>
<addresses>
<city>austin</city>
<state>TX</state>
</addresses>
<addresses>
<city>Hong Kong</city>
<state>Hong Kong</state>
</addresses>
</Customer>
But I get it always like with an extra "< addresses> < /addresses>" tag.
<Customer>
<id>1</id>
<name>Mighty Pulpo</name>
<addresses>
<addresses>
<city>austin</city>
<state>TX</state>
</addresses>
<addresses>
<city>Hong Kong</city>
<state>Hong Kong</state>
</addresses>
<addresses>
</Customer>
I am using below code to create XML
JaxbAnnotationModule jaxbAnnotationModule = new JaxbAnnotationModule();
XmlMapper mapper = new XmlMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
mapper.registerModule(jaxbAnnotationModule);
mapper.registerModule(new GuavaModule());
String xml = mapper.writeValueAsString(customer);
System.out.println(xml);
Please can some one help me? How can I remove the extra tag please. I have tried to use #XmlElement but it does not help help. TIA!!
Try the below code
#JacksonXmlRootElement(localName = "customer")
class Customer {
#JacksonXmlProperty(localName = "id")
private int id;
#JacksonXmlProperty(localName = "name")
private String name;
#JacksonXmlProperty(localName = "addresses")
#JacksonXmlElementWrapper(useWrapping = false)
private Address[] address;
// you can add it on getter method instead of declaration.
#JacksonXmlElementWrapper(useWrapping = false)
public Address[] getAddress(){
return address;
}
//getters, setters, toString
}
class Address {
#JacksonXmlProperty(localName = "city")
private String city;
#JacksonXmlProperty(localName = "state")
private String state;
// getter/setter
}
This setting changes default wrapping behavior, if you don't want to deal with annotation everywhere in your code.
XmlMapper mapper = new XmlMapper();
mapper.setDefaultUseWrapper(false);
Just to add to ManojP's answer, if adding the #JacksonXmlElementWrapper(useWrapping = false) on the declaration of your variable doesn't work (which was the case for me), adding it to the getter method will do the trick.

Skip XML nodes while unmarshaling XML with JAXB

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

Categories

Resources