Mapping nested xml elements to single Java Object - java

How would I map this
<urn:envelope xmlns:urn="urn:com.twinstrata.webservice:2.1">
<urn:encoded>
<urn:response>
<urn:license>
<urn:licenseTag>WHATEVER934</urn:licenseTag>
<urn:accountNumber>2016763117</urn:accountNumber>
<urn:licenseType>TRIAL</urn:licenseType>
<urn:licenseClass>Credentialed</urn:licenseClass>
<urn:volumeAllowed>Unlimited</urn:volumeAllowed>
<urn:volumeProvisioned>0</urn:volumeProvisioned>
<urn:snapshotLimit>Unlimited</urn:snapshotLimit>
<urn:snapshotLimitPerVolume>10</urn:snapshotLimitPerVolume>
<urn:status>Active</urn:status>
<urn:usedSpace>0</urn:usedSpace>
<urn:expirtationDate>2013-03-27 14:48:47.0</urn:expirtationDate>
<urn:storageLimit>Unlimited</urn:storageLimit>
</urn:license>
</urn:response>
</urn:encoded>
<urn:signature>Hl8rk2aTEsOkkq5e383LH0BqdFfmVcKIg9FuFEnnrlFk9fwYVEQwkrm/7MPM2Zmli2Um00L2Ab25tZg2w8pEzXyDsd+vwCAH0ypQwhIVPayDjgYKlYXbnkqG5S+7qiVbqD2qZDektuPoEWvaSdxO3ZgUibT+nnrO0kl6E7i4lB0=
</urn:signature>
to this
package com.folio3.bean;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "envelope" , namespace = "urn:com.twinstrata.webservice:2.1")
public class ResponseXML {
private String userName;
private String license;
private String signature;
private String licenseTag;
private String accountNumber;
private String licenseType;
private String licenseClass;
private String volumeAllowed;
private String volumeProvisioned;
private String publicKey;
#XmlElement(name = "userName" , namespace = "urn:com.twinstrata.webservice:2.1")
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
#XmlElement(name = "license" , namespace = "urn:com.twinstrata.webservice:2.1")
public String getLicense() {
return license;
}
public void setLicense(String license) {
this.license = license;
}
#XmlElement(name = "signature" , namespace = "urn:com.twinstrata.webservice:2.1")
public String getSignature() {
return signature;
}
public void setSignature(String signature) {
this.signature = signature;
}
#XmlElement(name = "licenseTag" , namespace = "urn:com.twinstrata.webservice:2.1")
public String getLicenseTag() {
return licenseTag;
}
public void setLicenseTag(String licenseTag) {
this.licenseTag = licenseTag;
}
#XmlElement(name = "accountNumber" , namespace = "urn:com.twinstrata.webservice:2.1")
public String getAccountNumber() {
return accountNumber;
}
public void setAccountNumber(String accountNumber) {
this.accountNumber = accountNumber;
}
#XmlElement(name = "licenseType" , namespace = "urn:com.twinstrata.webservice:2.1")
public String getLicenseType() {
return licenseType;
}
public void setLicenseType(String licenseType) {
this.licenseType = licenseType;
}
#XmlElement(name = "licenseClass" , namespace = "urn:com.twinstrata.webservice:2.1")
public String getLicenseClass() {
return licenseClass;
}
public void setLicenseClass(String licenseClass) {
this.licenseClass = licenseClass;
}
#XmlElement(name = "volumeAllowed" , namespace = "urn:com.twinstrata.webservice:2.1")
public String getVolumeAllowed() {
return volumeAllowed;
}
public void setVolumeAllowed(String volumeAllowed) {
this.volumeAllowed = volumeAllowed;
}
#XmlElement(name = "volumeProvisioned" , namespace = "urn:com.twinstrata.webservice:2.1")
public String getVolumeProvisioned() {
return volumeProvisioned;
}
public void setVolumeProvisioned(String volumeProvisioned) {
this.volumeProvisioned = volumeProvisioned;
}
#XmlElement(name = "publicKey" , namespace = "urn:com.twinstrata.webservice:2.1")
public String getPublicKey() {
return publicKey;
}
public void setPublicKey(String publicKey) {
this.publicKey = publicKey;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("ResponseXML [userName=");
builder.append(userName);
builder.append(", license=");
builder.append(license);
builder.append(", signature=");
builder.append(signature);
builder.append(", licenseTag=");
builder.append(licenseTag);
builder.append(", accountNumber=");
builder.append(accountNumber);
builder.append(", licenseType=");
builder.append(licenseType);
builder.append(", licenseClass=");
builder.append(licenseClass);
builder.append(", volumeAllowed=");
builder.append(volumeAllowed);
builder.append(", volumeProvisioned=");
builder.append(volumeProvisioned);
builder.append(", publicKey=");
builder.append(publicKey);
builder.append("]");
return builder.toString();
}
}
Currently , It maps only one property of XML , that is "signature".
For the sake of simplicity, I don't want to make other classes and nest the objects inside it. I just want to parse nested xml tags in single Java class.
How do I do that ?

Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
ResponseXML
You could use MOXy's #XmlPathextension to map your use case (see: http://blog.bdoughan.com/2010/09/xpath-based-mapping-geocode-example.html). Below is a partial mapping of your use case.
package forum15391077;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement(name = "envelope")
#XmlType(propOrder={"licenseTag", "accountNumber", "licenseType", "licenseClass", "volumeAllowed", "volumeProvisioned", "signature", "license", "publicKey", "userName"})
#XmlAccessorType(XmlAccessType.FIELD)
public class ResponseXML {
private String userName;
private String license;
private String signature;
#XmlPath("urn:encoded/urn:response/urn:license/urn:licenseTag/text()")
private String licenseTag;
#XmlPath("urn:encoded/urn:response/urn:license/urn:accountNumber/text()")
private String accountNumber;
#XmlPath("urn:encoded/urn:response/urn:license/urn:licenseType/text()")
private String licenseType;
#XmlPath("urn:encoded/urn:response/urn:license/urn:licenseClass/text()")
private String licenseClass;
#XmlPath("urn:encoded/urn:response/urn:license/urn:volumeAllowed/text()")
private String volumeAllowed;
#XmlPath("urn:encoded/urn:response/urn:license/urn:volumeProvisioned/text()")
private String volumeProvisioned;
private String publicKey;
}
package-info
We will use the package level #XmlSchema annotation to specify the namespace qualification (see: http://blog.bdoughan.com/2010/08/jaxb-namespaces.html). We will also use it to define the urn prefix which we leveraged in the #XmlPath annotation.
#XmlSchema(
namespace="urn:com.twinstrata.webservice:2.1",
elementFormDefault=XmlNsForm.QUALIFIED,
xmlns={
#XmlNs(namespaceURI = "urn:com.twinstrata.webservice:2.1", prefix = "urn")
}
)
package forum15391077;
import javax.xml.bind.annotation.*;
jaxb.properties
To specify MOXy as your JAXB provider you need to include a file called jaxb.properties in the same package as your domain model with the following entry (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html)
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
Since MOXy is a standard JAXB implementation, the standard JAXB runtime APIs are used.
package forum15391077;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(ResponseXML.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum15391077/input.xml");
ResponseXML response = (ResponseXML) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(response, System.out);
}
}
input.xml/Output
Below is a sample XML document based on the part of your use case that I mapped.
<?xml version="1.0" encoding="UTF-8"?>
<urn:envelope xmlns:urn="urn:com.twinstrata.webservice:2.1">
<urn:encoded>
<urn:response>
<urn:license>
<urn:licenseTag>WHATEVER934</urn:licenseTag>
<urn:accountNumber>2016763117</urn:accountNumber>
<urn:licenseType>TRIAL</urn:licenseType>
<urn:licenseClass>Credentialed</urn:licenseClass>
<urn:volumeAllowed>Unlimited</urn:volumeAllowed>
<urn:volumeProvisioned>0</urn:volumeProvisioned>
</urn:license>
</urn:response>
</urn:encoded>
<urn:signature>Hl8rk2aTEsOkkq5e383LH0BqdFfmVcKIg9FuFEnnrlFk9fwYVEQwkrm/7MPM2Zmli2Um00L2Ab25tZg2w8pEzXyDsd+vwCAH0ypQwhIVPayDjgYKlYXbnkqG5S+7qiVbqD2qZDektuPoEWvaSdxO3ZgUibT+nnrO0kl6E7i4lB0=
</urn:signature>
</urn:envelope>

I made it working by creating nested classes and by using proper JAXB annotations.

Related

JAXB XMLAdapter: Is there a way to convert this method into JAXB XmlAdapter

I have a JSON file that I am trying to convert into XML using the JAXB annotation approach. Everything is working fine now and I able to convert the JSON to XML. Now I am trying to refactor the code a little bit so that my class would look clean. Hence, I am trying to remove the method which is present in my class and make it JAXB XMLAdapter so that it can be reused by other classes.
Basically I would like to move the XMLSupport method from CarInfo class to XMLAdapter. I am not sure how to populate the CarInfo objects when I move them to the XMLAdapter.
Following is my JSON file (it has been modified for simplicity purpose):
{
"brand": "Ferari",
"build": "Italy",
"engine": "Mercedes",
"year": "2021"
}
Following is the XML that I expect JAXB to provide: (Observe the carInfo tag which is not present in JSON but I need in XML to match the standard XSD)
<?xml version="1.0"?>
<Car>
<brand>Ferari</brand>
<build>Italy</build>
<carinfo>
<engine>Mercedes</engine>
<year>2021</year>
</carinfo>
</Car>
Following are the classes that I have: (Tha Car class that matches the JSON elements)
#XmlAccessorType(XmlAccessType.FIELD)
#XmlTransient
#XmlSeeAlso({MyCar.class});
public class Car{
private String brand;
private String build;
#XmlTransient
private String engine;
#XmlTransient
private String year;
//Getter, Setters and other consturctiores ommited
}
Following is MYCar class that builds the XML by adding the carInfo tag:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "Car")
#XmlType(name = "Car", propOrder = {"brand","build", "carInfo"})
public class MyCar extends Car{
#XmlElement(name="carInfo")
private CarInfo carInfo;
public MyCar xmlSupport() {
if(carInfo == null){
carInfo = new Carinfo();
}
carInfo.setEngine(getEngine);
carInfo.setYear(getYear());
return this;
}
}
Following is my CarInfo class which acts as a helper to build the additional tag around MyCar class:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(propOrder = {"engine","year"})
public class Carinfo{
private String engine;
private String year;
//Getter, Setters and other consturctiores ommited
}
Following is my Main class which actually builds the XML by using the JAXBCOntext
public class Main{
public static void main(String[] args){
JAXBContext context = JAXBContext.newInstance(MyCar.class);
Marshaller mar = context.createMarshaller();
mar.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
mar.marshal((MyCar).xmlSupport(), System.out);
System.out.println("-----------------");
}
}
Now coming back to my main question:
As we can see from MyCar class I have the XMLSupport method which is actually populating the CarInfo objects and then using that method I am creating the XML. Is there a way I can move this to XMLAdapter?
I tried creating the XMLAdapter but I am not sure how can I populate the CarInfo objects from the adapter:
public class MyCar extends Car{
#XmlElement(name="carInfo")
#XmlJavaTypeAdapter(ExtensionAdapter.class)
#XmlElement(name = "carInfo")
private CarInfo carInfo;
}
Following is my Adapter class I've tried:
public class ExtensionAdapter extends XmlAdapter<CarInfo, CarInfo> {
#Override
public CarInfo unmarshal(CarInfo valueType) throws Exception {
System.out.println("UN-MARSHALLING");
return null;
}
#Override
public CarInfo marshal(CarInfo boundType) throws Exception {
System.out.println("MARSHALLING");
System.out.println(boundType);
//I get boundType as NULL so I am not sure how to convert the xmlSupport Method to Adapter so I can use this adapter with multiple class
return null;
}
}
You don't need any adapters, you just need a well-defined POJO.
The trick is using getters and setters, not field access, so we can do delegation, and then use #JsonIgnore and #XmlTransient to control which getter/setter methods are used for JSON vs XML.
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
#XmlRootElement(name = "Car")
#XmlType(propOrder = { "brand", "build", "carinfo" })
#JsonPropertyOrder({ "brand", "build", "engine", "year" })
public final class Car {
#XmlType(propOrder = { "engine", "year" })
public static final class Info {
private String engine;
private String year;
public String getEngine() {
return this.engine;
}
public void setEngine(String engine) {
this.engine = engine;
}
public String getYear() {
return this.year;
}
public void setYear(String year) {
this.year = year;
}
#Override
public String toString() {
return "Info[engine=" + this.engine + ", year=" + this.year + "]";
}
}
private String brand;
private String build;
private Info carinfo;
public Car() {
// Nothing to do
}
public Car(String brand, String build, String engine, String year) {
this.brand = brand;
this.build = build;
this.carinfo = new Info();
this.carinfo.setEngine(engine);
this.carinfo.setYear(year);
}
public String getBrand() {
return this.brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getBuild() {
return this.build;
}
public void setBuild(String build) {
this.build = build;
}
#JsonIgnore // For XML, not JSON
public Info getCarinfo() {
if (this.carinfo == null)
this.carinfo = new Info();
return this.carinfo;
}
public void setCarinfo(Info info) {
this.carinfo = info;
}
#XmlTransient // For JSON, not XML
public String getEngine() {
return getCarinfo().getEngine();
}
public void setEngine(String engine) {
getCarinfo().setEngine(engine);
}
#XmlTransient // For JSON, not XML
public String getYear() {
return getCarinfo().getYear();
}
public void setYear(String year) {
getCarinfo().setYear(year);
}
#Override
public String toString() {
return "Car[brand=" + this.brand + ", build=" + this.build + ", carinfo=" + this.carinfo + "]";
}
}
Test
Car car = new Car("Ferari", "Italy", "Mercedes", "2021");
// Generate JSON
ObjectMapper jsonMapper = new ObjectMapper();
jsonMapper.enable(SerializationFeature.INDENT_OUTPUT);
String json = jsonMapper.writeValueAsString(car);
// Generate XML
JAXBContext jaxbContext = JAXBContext.newInstance(Car.class);
Marshaller xmlMarshaller = jaxbContext.createMarshaller();
xmlMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
String xml;
try (StringWriter writer = new StringWriter()) {
xmlMarshaller.marshal(car, writer);
xml = writer.toString();
}
// Print generated results
System.out.println(car);
System.out.println(json);
System.out.println(xml);
// Parse JSON
Car carFromJson = jsonMapper.readValue(json, Car.class);
System.out.println(carFromJson);
// Parse XML
Unmarshaller xmlUnmarshaller = jaxbContext.createUnmarshaller();
Car carFromXml = xmlUnmarshaller.unmarshal(new StreamSource(new StringReader(xml)), Car.class).getValue();
System.out.println(carFromXml);
Outputs
Car[brand=Ferari, build=Italy, carinfo=Info[engine=Mercedes, year=2021]]
{
"brand" : "Ferari",
"build" : "Italy",
"engine" : "Mercedes",
"year" : "2021"
}
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Car>
<brand>Ferari</brand>
<build>Italy</build>
<carinfo>
<engine>Mercedes</engine>
<year>2021</year>
</carinfo>
</Car>
Car[brand=Ferari, build=Italy, carinfo=Info[engine=Mercedes, year=2021]]
Car[brand=Ferari, build=Italy, carinfo=Info[engine=Mercedes, year=2021]]
As you can see, the generated JSON and XML is exactly what you wanted, and the last two lines of output shows that parsing works as well.

Unmarshal XML attribute to object value

I'm working on a school project where I have to bind some XML values from an API to a java object. I am able to get all the elements, I can't however get the attribute of a specific element. I've looked around for a solution, but couldn't find one.
I have this piece of XML code that I want to unmarshal with JAXB to a Java object. The attribute I would like to get is 'changes' in Departuretrack.
<Departures>
<DepartingTrain>
<Id>220</Id>
<DepartureTime>2017-03-07T11:03:00+0100</DepartureTime>
<DepartureTrack changes="false">5</DepartureTrack>
</DepartingTrain>
<DepartingTrain>
<Id>637</Id>
<DepartureTime>2017-03-07T11:18:00+0100</DepartureTime>
<DepartureTrack changes="false">12</DepartureTrack>
</DepartingTrain>
</Departures>
I do currently have this object, it does work for all the elements. I don't know how to get the attribute 'changes' and put it into this object.
#Entity
#Getter
#Setter
#NoArgsConstructor
#XmlRootElement(name="Departures")
#XmlAccessorType(XmlAccessType.FIELD)
public class Departure {
#Id
#GeneratedValue
private long id;
#XmlElement(name="Id")
private int routeNumber;
#XmlElement(name="DepartureTime")
private String departureTime;
#XmlElement(name="DepartureTrack")
private String departureTrack;
}
I create a list with all the departures with this object.
#Entity
#Getter
#Setter
#NoArgsConstructor
#XmlRootElement(name="Departures")
#XmlAccessorType(XmlAccessType.FIELD)
public class DepartureList {
#Id
#GeneratedValue
private long id;
#XmlElement(name="DepartingTrain")
#OneToMany
private List<Departure> departures = new ArrayList<>();
}
This is what my unmarshaller looks like.
// Returns all departures for a specific station
public DepartureList getDepartingTrains(String station){
try {
URL url = new URL("API URL" + station);
URLConnection urlConnection = url.openConnection();
urlConnection.setRequestProperty("Authorization", "Basic " + authStringEnc);
InputStream is = urlConnection.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
Unmarshaller unmarshaller = departureListJaxbContext.createUnmarshaller();
DepartureList departureList = (DepartureList) unmarshaller.unmarshal(isr);
return departureList;
} catch (JAXBException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// Throw Exception
return null;
}
Does anyone know how to get this attribute from the XML sheet and put it into the Java object?
Add the "changes" attribute under DepartureTrack JAXB Generated class as below:
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement
public class DepartureTrack {
#XmlAttribute
protected String changes;
#XmlValue;
protected String content;
}
You should have java classes like below
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.Root;
import java.math.BigDecimal;
import java.util.List;
#Root(name = "Departures")
public class Departures {
#ElementList(name = "DepartingTrain", inline = true, required = false)
List<DepartingTrain> departingTrain;
public List<DepartingTrain> getDepartingTrain() { return this.departingTrain; }
public void setDepartingTrain(List<DepartingTrain> _value) { this.departingTrain = _value; }
public static class DepartingTrain {
#Element(name="Id", required = false)
String id;
#Element(name="DepartureTime", required = false)
String departureTime;
#Element(name="DepartureTrack", required = false)
DepartureTrack departureTrack;
public String getId() { return this.id; }
public void setId(String _value) { this.id = _value; }
public String getDepartureTime() { return this.departureTime; }
public void setDepartureTime(String _value) { this.departureTime = _value; }
public DepartureTrack getDepartureTrack() { return this.departureTrack; }
public void setDepartureTrack(DepartureTrack _value) { this.departureTrack = _value; }
}
public static class DepartureTrack {
#Attribute(name="changes", required = false)
Boolean changes;
public Boolean getChanges() { return this.changes; }
public void setChanges(Boolean _value) { this.changes = _value; }
}
}
and there are few sites which provide to create java classes from the xml or json.
http://pojo.sodhanalibrary.com/Convert
http://rnevet.github.io/simple-xml-pojo-gen/
http://pojo.sodhanalibrary.com/pojoFromXSD.html

Java Unmarshal list of objects with a class wrapper with JAXB

From an XQuery performed by BaseX server I get a result like that:
<ProtocolloList>
<protocollo>
<numero>1</numero>
<data>2014-06-23</data>
<oggetto/>
<destinatario/>
<operatore/>
</protocollo>
...
</ProtocolloList>
And I need to convert this result in a List of Protocollo objects with JAXB so that I can show them with JList. Thus, following one of the discussions here I've declared the following classes:
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "protocollo")
public class Protocollo {
private int numero;
private String data;
private String oggetto;
private String destinatario;
private String operatore;
public Protocollo(String d, String o, String des, String op) {
this.data = d;
this.oggetto = o;
this.destinatario = des;
this.operatore = op;
}
public Protocollo() {
}
#XmlElement
public int getNumero() {
return numero;
}
public void setNumero(int numero) {
this.numero = numero;
}
#XmlElement
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
#XmlElement
public String getOggetto() {
return oggetto;
}
public void setOggetto(String oggetto) {
this.oggetto = oggetto;
}
#XmlElement
public String getDestinatario() {
return destinatario;
}
public void setDestinatario(String destinatario) {
this.destinatario = destinatario;
}
#XmlElement
public String getOperatore() {
return operatore;
}
public void setOperatore(String operatore) {
this.operatore = operatore;
}
}
and
import java.util.ArrayList;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "ProtocolloList")
public class ProtocolloList {
#XmlElementWrapper(name = "ProtocolloList")
#XmlElement(name = "protocollo")
private ArrayList<Protocollo> ProtocolloList;
public ArrayList<Protocollo> getProtocolloList() {
return ProtocolloList;
}
public void setProtocolloList(ArrayList<Protocollo> protocolloList) {
ProtocolloList = protocolloList;
}
}
and finally I execute the converion like that:
JAXBContext jaxbContext = JAXBContext.newInstance(Protocollo.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
StringReader reader = new StringReader(this.resultXML);
protocolli = (ProtocolloList) unmarshaller.unmarshal(reader);
And I keep on getting this exception:
unexpected element (uri:"", local:"ProtocolloList"). Expected elements are <{}protocollo>
I suppose I'm making some mistakes with annotations.
Can you help?
For your use case you do not need the #XmlElementWrapper annotation. This is because the ProtocolList element corresponds to your #XmlRootElement annotation. Then you need the #XmlElement annotation on the property to grab each of the list items.
#XmlRootElement(name = "ProtocolloList")
public class ProtocolloList {
private ArrayList<Protocollo> ProtocolloList;
#XmlElement(name = "protocollo")
public ArrayList<Protocollo> getProtocolloList() {
return ProtocolloList;
}
}
Note:
By default you should annotate the property. If you want to annotate the fields you should put #XmlAccessorType(XmlAccessType.FIELD) on your class.
UPDATE
You need to make sure your JAXBContext is aware of the root class. You can change your JAXBContext creation code to be the following:
JAXBContext jaxbContext = JAXBContext.newInstance(ProtocolloList.class);

Exception in thread "main" java.lang.NullPointerException when using Jaxb unmarshal/marshal

I'm using JAXB to unmarshal a given input Xml file into Java object
and then marashal it back to Xml String.
My Xml file looks like this:
<bpmn2:definitions xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" id="_Definitions_1">
<bpmn2:process id="_500441" name="process">
</bpmn2:process>
</bpmn2:definitions>
Definitions.class:
#XmlRootElement(namespace = "http://www.omg.org/spec/BPMN/20100524/MODEL")
public class Definitions {
#XmlAttribute
private String id;
#XmlElement(name = "bpmn2:process")
private Process process;
#XmlElement(name = "bpmndi:BPMNDiagram")
private Diagram diagram;
public Definitions() {
}
public Definitions(String id, Process process, Diagram diagram) {
this.id = id;
this.process = process;
this.diagram = diagram;
}
public Process getProcess() {
return process;
}
public Diagram getDiagram() {
return diagram;
}
public String getId() {
return id;
}
}
Process.class:
#XmlAccessorType(XmlAccessType.FIELD)
public class Process {
#XmlAttribute
private String id;
public Process() {
}
public Process(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
Model.class:
public class Model {
#XmlElement
private Process process;
public Model() {
}
public Model(String processId, Process p) {
this.id = processId;
this.process = p;
}
}
main method:
public static void main(String[] args) throws IOException, JSONException, JAXBException {
BpmnToJsonImport bj = new BpmnToJsonImport();
InputStream is = BpmnToJsonImport.class.getResourceAsStream("myXml.txt");
String Str = IOUtils.toString(is);
StringReader sr = new StringReader(Str);
JAXBContext context = JAXBContext.newInstance(Definitions.class, Model.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
Definitions d = (Definitions) unmarshaller.unmarshal(sr);
Model model = new Model(d.getProcess().getId(), d.getProcess());
StringWriter sw = new StringWriter();
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
marshaller.marshal(model, sw);
String str = sw.toString();
System.out.println(str);
}
Exactly when it tries to retrieve the process id using d.getProcess.getId I get the
java.lang.NullPointerException
You are mapping the namespace qualification incorrectly. You must not include the prefix in the element name.
#XmlElement(name = "BPMNDiagram")
private Diagram diagram;
To map the namespace qualification you can use the package level #XmlSchema annotation.
package-info.java
#XmlSchema(
namespace = "http://www.omg.org/spec/BPMN/20100524/MODEL",
elementFormDefault = XmlNsForm.QUALIFIED)
package example;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
For More Information
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html

Default namespace & complex package/data structure

I need create/read xml file using default namespace:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xmlBoo xmlns="http://www.example2.org/boo">
<customer>
<address>
<street>Wall Street</street>
</address>
<id>1</id>
<name>John</name>
</customer>
<someSpecificField>Specific data in Boo</ns2:someSpecificField>
</xmlBoo>
but I'm getting:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:xmlBoo xmlns:ns2="http://www.example2.org/boo">
<ns2:customer>
<address>
<street>Wall Street</street>
</address>
<id>1</id>
<name>John</name>
</ns2:customer>
<ns2:someSpecificField>Specific data in Boo</ns2:someSpecificField>
</ns2:xmlBoo>
I know about package level metadata, but this is not working in complex package structure:
I have defined model classes like Address:
package example.model;
public class Address {
private String street;
Customer:
package example.model;
public class Customer {
private long id;
private String name;
private Address address;
The parent class for common fields:
package example.xml;
#XmlTransient
public class Xml {
private Customer customer;
Then specific classes which holds data/structure of concrete xml XmlBoo:
package example.xml.boo;
#XmlRootElement
public class XmlBoo extends Xml {
private String someSpecificField;
XmlFoo:
package example.xml.foo;
#XmlRootElement
public class XmlFoo extends Xml {}
package-info.java is included in two mentioned packages example.xml.boo:
#XmlSchema(
namespace = "http://www.example2.org/boo",
elementFormDefault = XmlNsForm.QUALIFIED)
package example.xml.boo;
and example.xml.foo:
#XmlSchema(
namespace = "http://www.example2.org/foo",
elementFormDefault = XmlNsForm.QUALIFIED)
package example.xml.foo;
And finally main method:
package example;
public class Demo {
public static void main(String... args) {
generateBoo();
generateFoo();
}
public static void generateBoo() {
try {
JAXBContext jc = JAXBContext.newInstance(XmlBoo.class);
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
XmlBoo xmlBoo = new XmlBoo();
Customer customer = new Customer();
customer.setId(1);
customer.setName("John");
Address address = new Address();
address.setStreet("Wall Street");
customer.setAddress(address);
xmlBoo.setCustomer(customer);
xmlBoo.setSomeSpecificField("Specific data in Boo");
m.marshal(xmlBoo, System.out);
} catch (JAXBException e) {
e.printStackTrace();
}
}
public static void generateFoo() {
try {
JAXBContext jc = JAXBContext.newInstance(XmlFoo.class);
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
XmlFoo xmlFoo = new XmlFoo();
Customer customer = new Customer();
customer.setId(1);
customer.setName("John");
Address address = new Address();
address.setStreet("Wall Street");
customer.setAddress(address);
xmlFoo.setCustomer(customer);
m.marshal(xmlFoo, System.out);
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
I've tried both solutions like here and also without success.
It is possible remove & rename prefix if I have all classes in one package (and one package-info file)
It is possible rename but NOT remove prefix if I have complex package structure
Is there solution how I can remove ns2 prefix?
I'm using JDK7.
Solution how get (write & read xml) the needed result:
<?xml version="1.0" encoding="UTF-8"?>
<xmlBoo xmlns="http://www.example.org/boo" xmlns:c="http://www.example.org/customer" xmlns:a="http://www.example.org/address" xmlns:h="http://www.example.org/header">
<h:header>
<h:id>101</h:id>
</h:header>
<c:customer>
<c:id>1</c:id>
<c:name>Yen</c:name>
<a:address>
<a:street>Long street</a:street>
</a:address>
</c:customer>
<someBooSpecificField>Specific data in Boo</someBooSpecificField>
</xmlBoo>
for root element and its "simple" children is used default namespace (without prefix)
for complex (objects in java) children are used different namespaces (mapped to different prefixes)
model classes are in different packages
So here is the solution:
Define MOXy implementation of JAXB, file: jaxb.properties
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Create abstract class for common fields, define namespace for object, file Xml.java
package example.xml;
#XmlTransient
public abstract class Xml {
private Header header;
private Customer customer;
#XmlElement(namespace="http://www.example.org/header")
public Header getHeader() {
return header;
}
public void setHeader(Header header) {
this.header = header;
}
#XmlElement(namespace="http://www.example.org/customer")
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
Create "root" class, XmlBoo.java
package example.xml.boo;
#XmlRootElement
#XmlType(propOrder = {"header", "customer", "someBooSpecificField"})
public class XmlBoo extends Xml {
private String someBooSpecificField;
// getter & setter
}
Set namespace and QUALIFIED for "root" class, file: example.xml.boo.package-info.java
#XmlSchema(
namespace = "http://www.example.org/boo",
elementFormDefault = XmlNsForm.QUALIFIED)
package example.xml.boo;
Set QUALIFIED to generate prefix for children (the namespace will be overridden by namespace defined on the class, but it must be defined), file: example.model.package-info.java
#XmlSchema(
namespace = "http://www.example.org",
elementFormDefault = XmlNsForm.QUALIFIED)
package example.model;
Create Header.java
package example.model;
#XmlType(namespace = "http://www.example.org/header")
public class Header {
private long id;
// getter & setter
}
Create Customer.java
package example.model;
#XmlType(namespace = "http://www.example.org/customer", propOrder = {"id", "name", "address"})
public class Customer {
private long id;
private String name;
private Address address;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#XmlElement(namespace="http://www.example.org/address")
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
Create Address.java
package example.model;
#XmlType(namespace = "http://www.example.org/address")
public class Address {
private String street;
// getter & setter
}
Create MyNamespacePrefixMapper.java by extending org.eclipse.persistence.oxm.NamespacePrefixMapper
package example;
import org.eclipse.persistence.oxm.NamespacePrefixMapper;
public class MyNamespacePrefixMapper extends NamespacePrefixMapper {
private static final String BOO_PREFIX = ""; // DEFAULT NAMESPACE
private static final String BOO_URI = "http://www.example.org/boo";
private static final String FOO_PREFIX = ""; // DEFAULT NAMESPACE
private static final String FOO_URI = "http://www.example.org/foo";
private static final String HEADER_PREFIX = "h";
private static final String HEADER_URI = "http://www.example.org/header";
private static final String CUSTOMER_PREFIX = "c";
private static final String CUSTOMER_URI = "http://www.example.org/customer";
private static final String ADDRESS_PREFIX = "a";
private static final String ADDRESS_URI = "http://www.example.org/address";
#Override
public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
switch (namespaceUri) {
case BOO_URI:
return BOO_PREFIX;
case FOO_URI:
return FOO_PREFIX;
case HEADER_URI:
return HEADER_PREFIX;
case CUSTOMER_URI:
return CUSTOMER_PREFIX;
case ADDRESS_URI:
return ADDRESS_PREFIX;
default:
return null;
}
}
}
Create XML
public static void generateBoo() {
try {
JAXBContext jc = JAXBContext.newInstance(XmlBoo.class);
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
m.setProperty(MarshallerProperties.NAMESPACE_PREFIX_MAPPER, new MyNamespacePrefixMapper());
XmlBoo xmlBoo = new XmlBoo();
Header header = new Header();
header.setId(101);
xmlBoo.setHeader(header);
Customer customer = new Customer();
customer.setId(1);
customer.setName("Yen");
Address address = new Address();
address.setStreet("Long street");
customer.setAddress(address);
xmlBoo.setCustomer(customer);
xmlBoo.setSomeBooSpecificField("Specific data in Boo");
m.marshal(xmlBoo, System.out);
m.marshal(xmlBoo, new File("xml_boo.xml"));
} catch (JAXBException e) {
e.printStackTrace();
}
}
Read XML
public static void readBoo() {
Object element = null;
try {
JAXBContext jc = JAXBContext.newInstance(XmlBoo.class);
Unmarshaller u = jc.createUnmarshaller();
element = u.unmarshal(new File("xml_boo.xml"));
} catch (JAXBException e) {
e.printStackTrace();
}
if (element instanceof XmlBoo) {
XmlBoo xmlBoo = (XmlBoo) element;
Customer customer = xmlBoo.getCustomer();
System.out.println("INFO | xmlBoo field: [" + xmlBoo.getSomeBooSpecificField() + "]");
System.out.println("INFO | customer name: [" + customer.getName() + "]");
System.out.println("INFO | address street: [" + customer.getAddress().getStreet() + "]");
}
}
I used EclipseLink MOXy JAXB implementation instead of RI Metro JAXB and now it works. So it looks that in Metro is bug.
Perfect tutorial by Blaise Doughan: JAXB & Namespace prefixes
You will need to have a package-info annotation with a #XmlSchema annotation for each package in your domain model each specifying the same namespace qualification to get the desired XML.

Categories

Resources