ignore root element in JAXB - java

I'm trying to parse an xml file using JAXB.
My problem is that I need to skip the root node, If I delete it from the xml file I get what I need, otherwise - I get an empty object.
I'll give a simplified xml and my code (It behaves the same way):
XML:
<?xml version="1.0" encoding="utf-8"?>
<Root>
<!-- <Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="Office.xsd">
-->
<Office>
<Employees>
<Employee>
<name>George</name>
<rank>3</rank>
</Employee>
<Employee>
<name>Michael</name>
<rank>5</rank>
</Employee>
<Employee>
<name>Jeff</name>
<rank>1</rank>
</Employee>
<Employee>
<name>Todd</name>
<rank>7</rank>
</Employee>
<Employee>
<name>Jessica</name>
<rank>5</rank>
</Employee>
</Employees>
</Office>
</Root>
Office class:
import javax.xml.bind.annotation.*;
import java.util.Vector;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Office {
#XmlElementWrapper(name = "Employees")
#XmlElement(name = "Employee")
private Vector<Employee> employees;
}
Employee class:
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
#XmlAccessorType(XmlAccessType.FIELD)
public class Employee {
#XmlElement(name="name")
private String name;
#XmlElement(name="rank")
private int rank;
public void promote() {
rank++;
}
}
Driver:
import javax.xml.stream.*;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.Marshaller;
import java.io.FileReader;
public class Driver {
public static void main (String[] args) {
parseOffice();
}
public static void parseOffice() {
try {
XMLInputFactory f = XMLInputFactory.newInstance();
XMLStreamReader reader = f.createXMLStreamReader(new FileReader("Office.xml"));
JAXBContext context = JAXBContext.newInstance(Office.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
Office office = unmarshaller.unmarshal(reader, Office.class).getValue();
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(office, System.out);
}
catch (Exception e) {
e.printStackTrace();
}
}
}

You can parse the XML with a StAX XMLStreamReader, then advance it to the element you want to unmarshal, and then unmarshal it.
I posted a full example that should help on the related question linked below:
How to unmarshall SOAP response using JAXB if namespace declaration is on SOAP envelope?

Why don't you create a generic root element?
#XmlRootElement(name="Root" ...)
public class Root {
#XmlAnyElement(lax=true)
private Object content;
}
Add it to your context and unmarshal. You should get a JAXBElement<Office> as content.

Simply add the root class in hierarcy. And get Office class from Root class.
Root Class:-
import javax.xml.bind.annotation.*;
import java.util.Vector;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
#XmlElement(name = "Office")
private Office office;
}
Office class
import javax.xml.bind.annotation.*;
import java.util.Vector;
#XmlAccessorType(XmlAccessType.FIELD)
public class Office {
#XmlElementWrapper(name = "Employees")
#XmlElement(name = "Employee")
private Vector<Employee> employees;
}
Change in parse method :-
JAXBContext context = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
Root root = unmarshaller.unmarshal(reader, Root.class).getValue();
Office office = root.getOffice();

Related

Object to xml tag and format soap envelope in Spring Boot

I am new in Spring Boot application i face an issue with xml format to soap envelope like below detail:
I need to convert object from java class to xml and format xml tag to soap envelop like below:
Bookstore.java
package com.example.wsdltojavaclass.xmlrequest;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSchema;
import javax.xml.bind.annotation.XmlType;
import javax.xml.namespace.QName;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "", namespace = "http://schemas.xmlsoap.org/soap/envelope/")
public class Bookstore {
#XmlElementWrapper(name = "bookList")
#XmlElement(name = "book")
private List < Book > bookList;
private String name;
private String location;
// public void setBookList(List < Book > bookList) {
// this.bookList = bookList;
// }
//
// public List < Book > getBooksList() {
// return bookList;
// }
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
}
My Controller:
#GetMapping("/xml")
public void newroute() throws JAXBException, FileNotFoundException, JAXBException {
List<Book> bookList = new ArrayList<Book>();
Bookstore bookstore = new Bookstore();
bookstore.setName("Amazon Bookstore");
bookstore.setLocation("Newyorkt");
convertObjectToXML(bookstore);
}
private static void convertObjectToXML(Bookstore bookstore) throws JAXBException, FileNotFoundException {
JAXBContext context = JAXBContext.newInstance(Bookstore.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
m.marshal(bookstore, System.out);
m.marshal(bookstore, new File(BOOKSTORE_XML));
}
And here is my result:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2: xmlns:ns2="http://schemas.xmlsoap.org/soap/envelope/">
<name>Amazon Bookstore</name>
<location>Newyorkt</location>
</ns2:>
My expected result i need to change remove <?xml version="1.0" encoding="UTF-8" standalone="yes"?> and change format to
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:web="http://www.dataaccess.com/webservicesserver/">
<soap:Header/>
<soap:Body>
<web:name>Amazon Bookstore</web:name>
<web:location>Newyorkt</web:location>
</soap:Body>
</soap:Envelope>
So how to change from ns2 to soap envelope?

xmlns has been already bound to . Rebinding it to http://entreprise.uk/ns is an error

I'm using spring batch to generate xml files.
My writer looks like :
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setClassesToBeBound(Person.class);
StaxEventItemWriter<Person> itemWriter = new StaxEventItemWriter<>();
itemWriter.setRootTagName("Persons");
itemWriter.setMarshaller(marshaller);
itemWriter.setRootElementAttributes(new HashMap<String, String>() {{
put("xmlns", "http://entreprise.uk/ns");
}});
itemWriter.setResource(new FileSystemResource(Paths.get("personOutput.xml").toFile()));
itemWriter.afterPropertiesSet();
return itemWriter;
And the person class :
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "Person")
public class Person {
//...
}
When I run the batch I get this error :
Caused by: javax.xml.stream.XMLStreamException: xmlns has been already bound to . Rebinding it to http://entreprise.uk/ns is an error
Anyone knows how to fix it ? I need to see xmlns attribut at the root element like :
<?xml version="1.0" encoding="UTF-8"?>
<Persons xmlns="http://entreprise.uk/ns">
<person>...</person>
</Persons>
I'm using spring-boot-starter-batch:2.3.5.RELEASE
To add namespace at the root level, you have to modify the rootTagName in your configuration.
rootTagName("{http://entreprise.uk/ns}Persons")
Hope this solves your problem.
I would personally go with a container class called Persons that has an array/list of Person.
#XmlRootElement(name = "Persons", namespace = "http://entreprise.uk/ns")
#XmlAccessorType(XmlAccessType.FIELD)
public class Persons {
#XmlElement(name = "Person")
private List<Person> persons;
//...
}
This should also remove the need for some of the configurations you currently have to set.
There are 2 ways to solve this issue:
Method-1
Using the package-info file:
XML:
<?xml version="1.0" encoding="UTF-8"?>
<Persons xmlns="http://entreprise.uk/ns">
<person>
<firstName>Batman</firstName>
</person>
</Persons>
package-info.java:
#XmlSchema(
elementFormDefault = XmlNsForm.QUALIFIED,
namespace = "http://entreprise.uk/ns",
xmlns = {#XmlNs(prefix = "", namespaceURI = "http://entreprise.uk/ns")})
package stackover;
import jakarta.xml.bind.annotation.XmlNs;
import jakarta.xml.bind.annotation.XmlNsForm;
import jakarta.xml.bind.annotation.XmlSchema;
Persons:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "Persons")
#Data
public class Persons {
private Person person;
}
Person:
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class Person {
private String firstName;
}
Main:
public class Main {
public static void main(String[] args) throws JAXBException, XMLStreamException {
final InputStream inputStream = Main.class.getClassLoader().getResourceAsStream("students.xml");
final XMLStreamReader xmlStreamReader = XMLInputFactory.newInstance().createXMLStreamReader(inputStream);
final Unmarshaller unmarshaller = JAXBContext.newInstance(Persons.class).createUnmarshaller();
final Persons persons = unmarshaller.unmarshal(xmlStreamReader, Persons.class).getValue();
System.out.println(persons.toString());
Marshaller marshaller = JAXBContext.newInstance(Persons.class).createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(persons, System.out);
}
}
Output:
Persons(person=Person(firstName=Batman))
<Persons xmlns="http://entreprise.uk/ns">
<person>
<firstName>Batman</firstName>
</person>
</Persons>
Method-2
Using the prefix with namespace URI for XML and adding to the root class:
XML:
<?xml version="1.0" encoding="UTF-8"?>
<Persons xmlns:ns0="http://entreprise.uk/ns">
<person>
<firstName>Batman</firstName>
</person>
</Persons>
Persons:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "Persons", namespace = "http://entreprise.uk/ns")
#Data
public class Persons {
private Person person;
}
Person:
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class Person {
private String firstName;
}
Main:
public class Main {
public static void main(String[] args) throws JAXBException, XMLStreamException {
final InputStream inputStream = Main.class.getClassLoader().getResourceAsStream("students.xml");
final XMLStreamReader xmlStreamReader = XMLInputFactory.newInstance().createXMLStreamReader(inputStream);
final Unmarshaller unmarshaller = JAXBContext.newInstance(Persons.class).createUnmarshaller();
final Persons persons = unmarshaller.unmarshal(xmlStreamReader, Persons.class).getValue();
System.out.println(persons.toString());
Marshaller marshaller = JAXBContext.newInstance(Persons.class).createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(persons, System.out);
}
}
Output:
Persons(person=Person(firstName=Batman))
<ns0:Persons xmlns:ns0="http://entreprise.uk/ns">
<person>
<firstName>Batman</firstName>
</person>
</ns0:Persons>

Create XML string in java with custom namespace

I want to generate xml string in java programmatically whose name space is custom as shown below
and all data must come dynamically in xml.How can I achieve something like this?
<faxml xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="test.xsd">
<person>
<name>ABC</name>
</person>
</faxml>
I have gone through examples like this https://howtodoinjava.com/jaxb/write-object-to-xml/ but here when xml generated its starting line is
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
but I want start and end tag <faxml> and namespaces as I mentioned in my sample code as output
Here is one approach:
First, here is my class representing the required XML data:
package org.ajames.jaxb.persons;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "")
#XmlRootElement(name = "faxml")
public class Faxml {
private Person person;
public static class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
}
I chose to nest the Person class inside the root class - just for this test. There are other ways to arrange these classes, of course, so they are not nested.
Then I define the package-info for org.ajames.jaxb.persons as follows:
#XmlSchema(
namespace = "",
elementFormDefault = XmlNsForm.UNSET,
xmlns = {
#XmlNs(prefix = "", namespaceURI = "http://www.w3.org/2001/XMLSchema-instance")
})
package org.ajames.jaxb.persons;
import javax.xml.bind.annotation.XmlSchema;
import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlNsForm;
To process the data from a Java object to XML, I use the following test data:
Faxml.Person person = new Faxml.Person();
person.setName("ABC");
Faxml faxml = new Faxml();
faxml.setPerson(person);
The JAXB context and marshaller are as follows, assuming we are writing the XML to a Java String, for this test:
JAXBContext jaxbContext = JAXBContext.newInstance(Faxml.class);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_NO_NAMESPACE_SCHEMA_LOCATION, "test.xsd");
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
StringWriter stringWriter = new StringWriter();
marshaller.marshal(faxml, stringWriter);
String xml = stringWriter.toString();
System.out.println(xml);
The resulting XML is:
<faxml xsi:noNamespaceSchemaLocation="test.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<person>
<name>ABC</name>
</person>
</faxml>
String name= "name"
String createXml="<faxml xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"//
xsi:noNamespaceSchemaLocation="test.xsd">"//
+"<person>"//
+"<name>"+name+"</name>"//
+"</>person">"//
+"</faxml>";
Sysout(createXml);
get the name in a variable. Hard code these lines and insert it like this..

jaxb unmarshalling with namespace

This is my xml, need to convert it into java. I had used jaxb
<?xml version="1.0"?>
<lm:order Id="PLG24M240U" JD="" aCount="2" SUCount="1" xmlns:lm="http://www.ae.com/Event/Load">
<lm:master>
<lm:ID>3</lm:ID>
<lm:Number>313</lm:Number>
<lm:ANumber>323</lm:ANumber>
</lm:master>
<lm:detail>
<lm:ID>3</lm:ID>
<lm:Number>3131</lm:Number>
<lm:ANumber>3232</lm:ANumber>
</lm:detail>
<lm:detail>
<lm:ID>3</lm:ID>
<lm:Number>3131</lm:Number>
<lm:ANumber>3232</lm:ANumber>
</lm:detail>
<lm:detail>
<lm:ID>3</lm:ID>
<lm:Number>313</lm:Number>
<lm:ANumber>323</lm:ANumber>
</lm:detail>
</lm:order>
And throwing the following exception
javax.xml.bind.UnmarshalException: unexpected element (uri:"http://www.ae.com/Event/Load", local:"Order"). Expected elements are <{}lm:Order>
This is my unmarshalling code
jaxbContext = JAXBContext.newInstance(Order.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Order order = (Order) jaxbUnmarshaller.unmarshal(file);
System.out.println(order );
Order Pojo class
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "lm:Order")
public class OrderPay {
#XmlAttribute
private String Id;
#XmlAttribute
private String JD;
#XmlAttribute
private String aCount;
#XmlAttribute
private String pCount;
/*#XmlElement
private Master master;
#XmlElement
private List<Detail> details = new ArrayList<Detail>();*/
}
Can you please help me in reading also, currently reading through file, need to read as an XML String.
The namespace attribute xmlns:lm="http://www.ae.com/Event/Load" might be the culprit here. In order to specify the namespace prefix, you can add the #XmlSchema annotation to a package-info.java file like this:
#XmlSchema(
namespace="http://www.ae.com/Event/Load",
elementFormDefault=XmlNsForm.QUALIFIED),
xmlns={#XmlNs(prefix="lm", namespaceURI="http://www.ae.com/Event/Load")})
package your.package;
import javax.xml.bind.annotation.*;

Jaxb complex xml unmarshall

I am having issues to unmarshall nested xml below. Can someone please advise if I am missing something.
body tag can contain any Jaxb anotated obj.
Do I have to create a custom adapter for marshalling/unmarshalling such xml?
Input XML
<?xml version="1.0" encoding="UTF-8"?>
<serviceRq xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="serviceRq">
<body>
<createRq>
<id>1234</id>
</createRq>
</body>
</serviceRq>
My Jaxb-annotated classes are:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "serviceRq")
public class ServiceRq{
private Object body;
<!-- getters and setters omitted-->
}
Here, body can be any jaxb annotated object, in this case its CreateRq.
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "createRq")
public class CreateRq{
private String id;
<!-- getters and setters omitted-->
}
I am looking for a generic way to support any Jaxb annotated object in body of the input xml.
You could use a #XmlAnyElement(lax=true) and an XmlAdapter to handle this use case:
ServiceRq
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "serviceRq")
public class ServiceRq{
#XmlJavaTypeAdapter(value=BodyAdapter.class)
private Object body;
// getters and setters omitted
}
BodyAdapter
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class BodyAdapter extends XmlAdapter<Body, Object>{
#Override
public Object unmarshal(Body v) throws Exception {
return v.getValue();
}
#Override
public Body marshal(Object v) throws Exception {
Body body = new Body();
body.setValue(v);
return body;
}
}
Body
import javax.xml.bind.annotation.XmlAnyElement;
public class Body {
private Object value;
#XmlAnyElement(lax=true)
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
CreateRq
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "createRq")
public class CreateRq{
private String id;
// getters and setters omitted
}
Demo
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(ServiceRq.class);
System.out.println(jc);
Unmarshaller unmarshaller = jc.createUnmarshaller();
ServiceRq serviceRq = (ServiceRq) unmarshaller.unmarshal(new File("input.xml"));
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(serviceRq, System.out);
}
}
For More Information
http://bdoughan.blogspot.com/2010/08/using-xmlanyelement-to-build-generic.html
http://bdoughan.blogspot.com/2010/07/xmladapter-jaxbs-secret-weapon.html
http://bdoughan.blogspot.com/2010/12/jaxb-and-immutable-objects.html
http://bdoughan.blogspot.com/2010/12/represent-string-values-as-element.html
You could use #XmlAnyElement(lax=true) and the #XmlPath extension in EclipseLink JAXB (MOXy) to handle this use case (Note: I'm the MOXy lead). For an approach that would work with any JAXB implementation (Metro, MOXy, JaxMe, etc) see: Jaxb complex xml unmarshall.
ServiceRq
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "serviceRq")
public class ServiceRq{
#XmlPath("body/createRq")
#XmlAnyElement(lax=true)
private Object body;
// getters and setters omitted
}
CreateRq
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "createRq")
public class CreateRq{
private String id;
// getters and setters omitted
}
Demo
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(ServiceRq.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
ServiceRq serviceRq = (ServiceRq) unmarshaller.unmarshal(new File("input.xml"));
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(serviceRq, System.out);
}
}
jaxb.properties
To use MOXy as your JAXB provider you must include a file named jaxb.properties in the same package as your domain model with the following entry:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
For More Information
http://bdoughan.blogspot.com/2010/08/using-xmlanyelement-to-build-generic.html
http://bdoughan.blogspot.com/2011/05/specifying-eclipselink-moxy-as-your.html
http://bdoughan.blogspot.com/2010/09/xpath-based-mapping-geocode-example.html
http://bdoughan.blogspot.com/2011/03/map-to-element-based-on-attribute-value.html

Categories

Resources