I have a class called Building.
It has a list of BuildingBenchAssociation records (List<BuildingBenchAssociation> benches)
BuildingBenchAssociation has a composite id made up of buildingId and benchId
The ID is represented by separate class called BuildingBenchAssociationPKwhich has only two properties - buildingId and benchId
This is the output I get when I marshal a Building instance
<building buildingId="9">
<benches>
DOMRecord(<?xml version="1.0" encoding="UTF-8"?><buildingBenchAssociation><benchId>245865</benchId><buildingId>9</buildingId></buildingBenchAssociation>)
</benches>
<benches>
DOMRecord(<?xml version="1.0" encoding="UTF-8"?><buildingBenchAssociation><benchId>245866</benchId><buildingId>9</buildingId></buildingBenchAssociation>)
</benches>
<benches>
But I don't want DOMRecord(<?xml version="1.0" encoding="UTF-8"?> to appear in the output. Required output is something like this:
<building buildingId="9">
<benches>
<buildingBenchAssociation><benchId>245865</benchId><buildingId>9</buildingId></buildingBenchAssociation>
</benches>
<benches>
<buildingBenchAssociation><benchId>245866</benchId><buildingId>9</buildingId></buildingBenchAssociation>
</benches>
<benches>
What's wrong and how do I correct it? I am using the Eclipselink MOXy library.
Classes for reference:
Class 1
#Entity
#Table(name="building")
#XmlRootElement
public class Building implements Serializable {
....
private List<BuildingBenchAssociation> benchs = new ArrayList<BuildingBenchAssociation>();
#XmlIDREF
#OneToMany(mappedBy="building")
public List<BuildingBenchAssociation> getBenchs() {
return benchs;
}
public void setBenchs(List<BuildingBenchAssociation> benchs) {
this.benchs = benchs;
}
}
Class 2
#Entity
#Table(name="building_bench_rel")
#XmlRootElement
public class BuildingBenchAssociation implements Serializable {
private static final long serialVersionUID = 1L;
private BuildingBenchAssociationPK idx;
private Bench bench;
private Building building;
private byte alertFlags;
private byte status;
public BuildingBenchAssociation() {
idx=new BuildingBenchAssociationPK();
}
#XmlID
#XmlPath(".")
#Id
public BuildingBenchAssociationPK getIdx() {
return this.idx;
}
public void setIdx(BuildingBenchAssociationPK id) {
this.idx = id;
}
#Column(name="ALERT_FLAGS")
public byte getAlertFlags() {
return this.alertFlags;
}
public void setAlertFlags(byte alertFlags) {
this.alertFlags = alertFlags;
}
#Column(name="STATUS", insertable=false, updatable=false)
public byte getStatus() {
return this.status;
}
public void setStatus(byte status) {
this.status = status;
}
#XmlIDREF
#ManyToOne
#JoinColumn(name="BENCH_ID",insertable=false,updatable=false)
public Bench getBench() {
return bench;
}
public void setBench(Bench bench) {
this.bench = bench;
this.idx.setBenchId(bench==null?null:bench.getBenchId());
}
#XmlIDREF
#ManyToOne
#JoinColumn(name="BUILDING_ID",insertable=false,updatable=false)
public Building getBuilding() {
return building;
}
public void setBuilding(Building building) {
this.building = building;
this.idx.setBuildingId(building==null?null:building.getBuildingId());
}
}
Class 3
#Embeddable
#XmlRootElement
public class BuildingBenchAssociationPK implements Serializable {
...
private Integer buildingId;
private Integer benchId;
public BuildingBenchAssociationPK() {
}
#XmlKey
#Column(name="BUILDING_ID")
public Integer getBuildingId() {
return this.buildingId;
}
public void setBuildingId(Integer buildingId) {
this.buildingId = buildingId;
}
#XmlKey
#Column(name="BENCH_ID")
public Integer getBenchId() {
return this.benchId;
}
public void setBenchId(Integer benchId) {
this.benchId = benchId;
}
}
Below is how you can currently map this use case using MOXy. I have opened the following enhancement request to make this use case easier to map:
http://bugs.eclipse.org/407460
REFERENCED OBJECT
Embedded ID (EmployeeId)
Below is an example of an embedded ID class:
import java.math.BigDecimal;
import javax.persistence.*;
import javax.xml.bind.annotation.*;
#Embeddable
#XmlAccessorType(XmlAccessType.FIELD)
public class EmployeeId {
#Column(name="E_ID")
BigDecimal eId;
String country;
}
Class with Embedded ID (Employee)
We want to use the embedded ID class for as the key in an XML relationship. Currently MOXy does not allow this to be done via annotations, so we will leverage the #XmlCustomizer annotation to programmatically modify the metadata.
import java.util.List;
import javax.persistence.*;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.*;
#Entity
#IdClass(EmployeeId.class)
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlCustomizer(EmployeeCustomizer.class)
public class Employee {
#EmbeddedId
#XmlPath(".")
EmployeeId id;
#OneToMany(mappedBy="contact")
List<PhoneNumber> contactNumber;
}
Customize Mapping Metadata for Employee (EmployeeCustomizer)
In the customizer class we will specify the XPaths for the mappings that compose the key on the embedded class.
import org.eclipse.persistence.config.DescriptorCustomizer;
import org.eclipse.persistence.descriptors.ClassDescriptor;
public class EmployeeCustomizer implements DescriptorCustomizer {
#Override
public void customize(ClassDescriptor descriptor) throws Exception {
descriptor.addPrimaryKeyFieldName("eId/text()");
descriptor.addPrimaryKeyFieldName("country/text()");
}
}
REFERRING OBJECT
PhoneNumber
We are also going to need to programatically add the mapping based on the composite key, so once again we will use the #XmlCustomizer annotation.
import javax.persistence.*;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.*;
#Entity
#XmlAccessorType(XmlAccessType.FIELD)
#XmlCustomizer(PhoneNumberCustomizer.class)
public class PhoneNumber {
#ManyToOne
#JoinColumns({
#JoinColumn(name="E_ID", referencedColumnName = "E_ID"),
#JoinColumn(name="E_COUNTRY", referencedColumnName = "COUNTRY")
})
Employee contact;
}
Customize Mapping Metadata for PhoneNumber (PhoneNumberCustomizer)
In this customizer we will remove the default mapping, and programatically create the new one based on the composite keys.
import org.eclipse.persistence.config.DescriptorCustomizer;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.oxm.mappings.XMLObjectReferenceMapping;
public class PhoneNumberCustomizer implements DescriptorCustomizer {
#Override
public void customize(ClassDescriptor descriptor) throws Exception {
descriptor.removeMappingForAttributeName("contact");
XMLObjectReferenceMapping contactMapping = new XMLObjectReferenceMapping();
contactMapping.setAttributeName("contact");
contactMapping.setReferenceClass(Employee.class);
contactMapping.addSourceToTargetKeyFieldAssociation("contact/#eID", "eId/text()");
contactMapping.addSourceToTargetKeyFieldAssociation("contact/#country", "country/text()");
descriptor.addMapping(contactMapping);
}
}
DEMO CODE
The following demo code can be used to demonstrate that everything works:
Demo
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Employee.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("input.xml");
Employee employee = (Employee) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(employee, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8"?>
<employee>
<eId>10</eId>
<country>Canada</country>
<contactNumber>
<contact eID="10" country="Canada"/>
</contactNumber>
<contactNumber>
<contact eID="10" country="Canada"/>
</contactNumber>
</employee>
FOR MORE INFORMATION
http://wiki.eclipse.org/EclipseLink/Examples/MOXy/JPA/EmbeddedIdClass
Related
I wan't to deserialize XML to my POJO but something doing wrong...
My POJO class:
#Builder
#ToString
#AllArgsConstructor
#NoArgsConstructor
#XmlRootElement(name="taxi")
#XmlAccessorType(XmlAccessType.PROPERTY)
#XmlType(propOrder = {"id", "name", "phone", "citiesId"})
public class TaxiEntity {
#Getter #Setter
private Integer id;
#Getter #Setter
private String name;
#Getter #Setter
private String phone;
#Singular("city")
private Set<Integer> citiesId = new HashSet<>();
#XmlElementWrapper(name="cities_id")
#XmlElement(name="city_id")
public void setCitiesId(Set<Integer> citiesId) {
System.out.println("setCitiesId()");
this.citiesId = citiesId;
}
public Set<Integer> getCitiesId() {
System.out.println("getCitiesId()");
return new HashSet<>(citiesId);
}
}
Marshalling example:
JAXBContext context = JAXBContext.newInstance(TaxiEntity.class);
TaxiEntity entity = TaxiEntity.builder().
id(5).
name("my city").
phone("12345678").
city(1).
city(5).
build();
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(entity, new File("entity.xml"));
XML output:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<taxi>
<id>5</id>
<name>my city</name>
<phone>12345678</phone>
<cities_id>
<city_id>1</city_id>
<city_id>5</city_id>
</cities_id>
</taxi>
Unmarshalling example:
JAXBContext context = JAXBContext.newInstance(TaxiEntity.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
TaxiEntity entity = (TaxiEntity) unmarshaller.unmarshal(new File("entity.xml"));
System.out.println(entity);
Console output:
getCitiesId()
getCitiesId()
TaxiEntity(id=5, name=my city, phone=12345678, citiesId=[])
Process finished with exit code 0
As you can see, citiesId is empty.
It happens because JAXB unmarshalling calling the getter (copy of field in my case)
and trying to set values into a copy of collection.
How to make it create a collection and set it via setter?
P.S. In my real bussiness object, i have collect IDs in getter from DB entities, and cannot return collection in getter.
Thanks!
---- Edited last time -----
import java.io.File;
import java.util.Set;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import lombok.Builder;
import lombok.Singular;
import lombok.ToString;
#Builder
#ToString
#XmlRootElement(name = "taxi")
#XmlType(name="taxi", propOrder = { "id", "name", "phone", "citiesId" })
public class TaxiEntity {
private Integer id;
private String name;
private String phone;
#Singular("city")
private Set<Integer> citiesId;
public TaxiEntity() {
}
public TaxiEntity(Integer id, String name, String phone, Set<Integer> citiesId) {
System.out.println("Hello");
this.id = id;
this.name = name;
this.phone = phone;
this.citiesId = citiesId;
}
#XmlElementWrapper(name = "cities_id")
#XmlElement(name = "city_id")
public void setCitiesId(Set<Integer> citiesId) {
System.out.println("I should be calling during deserialization" + citiesId);
this.citiesId = citiesId;
}
#XmlElement
public void setId(Integer id) {
this.id = id;
}
#XmlElement
public void setName(String name) {
this.name = name;
}
#XmlElement
public void setPhone(String phone) {
this.phone = phone;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public String getPhone() {
return phone;
}
public Set<Integer> getCitiesId() {
System.out.println("Calling getter " + this.citiesId);
return citiesId;
}
public static void main(String[] args) {
try {
JAXBContext context = JAXBContext.newInstance(TaxiEntity.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
Unmarshaller unmarshaller = context.createUnmarshaller();
TaxiEntity entity = TaxiEntity.builder().id(5).name("my city").phone("12345678").city(1).city(5).build();
marshaller.marshal(entity, new File("C:/whee/entity.xml"));
System.out.println("Unmarshalling now ------");
TaxiEntity taxEntityWithSettersGetters = (TaxiEntity) unmarshaller.unmarshal(new File("C:/whee/entity.xml"));
System.out.println(taxEntityWithSettersGetters);
} catch (Exception e) {
e.printStackTrace();
}
}
Printout:
Hello
Calling getter [1, 5]
Unmarshalling now ------
Calling getter null
I should be calling during deserialization[]
Calling getter [1, 5]
TaxiEntity(id=5, name=my city, phone=12345678, citiesId=[1, 5])
During unmarshalling JAXB checks if your collection is null, if it is (It will call the setter for the first time to initialize it to empty), and you can see that in the log.
However, afterwards, it will use its internal logic to populate the collection (SET), initialize its type (New Set)*by using the Setter you have, and use the Set.add(xyz); to add (1), then (5).
The JAXB Logic invoked is found in class:
public abstract class Lister<BeanT,PropT,ItemT,PackT> {
//startPacking is calling to initialize the collection Set so it is empty
public T startPacking(BeanT bean, Accessor<BeanT, T> acc) throws AccessorException {
T collection = acc.get(bean);
if(collection==null) {
collection = ClassFactory.create(implClass);
if(!acc.isAdapted())
acc.set(bean,collection);
}
collection.clear();
return collection;
}
//Right way, this gets called afterwards (Before any of your TaxiEntity logic), to do addToPack(1), addToPack(5), <--- Now your Set has [1,5]
public void addToPack(T collection, Object o) {
collection.add(o);
}
Then, you see in the log, it calls getCitiesIds(), and you will see magically it has [1,5]
Its the way JAXB works with Collections. All other elements, their proper Setters are called.
See, JAXB does not call Setter method
You need to think of a different way of doing it, rather than dependending on the getter/setter. It did its job of unmarshalling the object from the XML file, the rest of the logic could be written in an external method.
I want to read in a xml-file based on jaxb to my objectoriented structure.
Lets say this is my xml-file:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<children xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<child xsi:type="girl">
<age>12</age>
<isdancing>true</isdancing>
</child>
<child xsi:type="boy">
<age>10</age>
<issoccerplayer>true</issoccerplayer>
</child>
</children>
children is some kind of wrapper element including multiple child elements. A child can either be a boy or a girl specified by xsi:type. These two classes have some elements in common (like age) and some different (excluding) elements (like isdancing or issoccerplayer)
To read the file, i have this method:
public static void main( String[] args ) throws JAXBException
{
JAXBContext jaxbContext;
jaxbContext = JAXBContext.newInstance(Children.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
File file = new File("C:/test.xml");
if (!file.exists()) System.out.println("File does not exist");
Children children = (Children) jaxbUnmarshaller.unmarshal(file);
System.out.println(children.toString());
}
My Children class looks like this:
#XmlRootElement(name="children")
#XmlAccessorType(XmlAccessType.FIELD)
public class Children {
#XmlElement(name="child")
private List<Child> childrenList;
public List<Child> getChildren() { return childrenList; }
public void setChildren(List<Child> children) {this.childrenList = children;}
#Override
public String toString() {
return ReflectionToStringBuilder.toString(this);
}
}
My Child class looks like this:
#XmlAccessorType(XmlAccessType.FIELD)
public class Child {
#XmlAttribute(name="xsi:type")
private XsiType xsiType;
private int age;
#XmlElement(name = "isdancing")
private boolean isDancing;
#XmlElement(name = "issoccerplayer")
private boolean isSoccerPlayer;
//Getter and setter for all fields
#Override
public String toString() {
return ReflectionToStringBuilder.toString(this);
}
}
And my XsiType class looks like this:
#XmlAccessorType(XmlAccessType.FIELD)
public class XsiType {
#XmlAttribute(name="xsi:type")
private String name;
#XmlValue
private String value;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getValue() { return value;
public void setValue(String value) { this.value = value; }
}
In my pom.xml i have included the following dependencies:
<dependencies>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
</dependencies>
My problem is now, that the output is ok, but the element xsiType of Child-class is always null or otherwise it ends up in IllegalAnnotationExceptions, which are related to XmlTest.model.Child.xsiType
So i expect there is a mistake by setting any kind of #Xml-Annotation. Can somebody help me by finding the mistake?
The target is to iterate of the list of children and decide at runtime (based on the xsiType), if this is a girl or a boy.
Thanks
You don't need your XsiType class.
You can just use String instead.
In your Child class
the xsiType attribute should look like this.
#XmlAttribute(name = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
private String xsiType;
Notice: in the #XmlAttribute annotation
use name = "type" (without the prefix xsi:)
specify the namespace parameter as given in your XML
by xmlns:xsi="..."
By the way:
Instead of typing the string "http://www.w3.org/2001/XMLSchema-instance"
you should better use the constant
XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI.
So your improved code would like this:
#XmlAttribute(name = "type", namespace = XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI)
private String xsiType;
xsi type is usually used to express references to concrete types. Jaxb can use xsi types without further workarounds.
Create a Boy and a Girl class that extend Children. (You might need to adjust the type names with #XmlType). With that, all elements with xsi:type=Girl will be bound to the class Girl
#XmlAccessorType(XmlAccessType.FIELD)
#XmlSeeAlso({ Boy.class, Girl.class }) // Either use #XmlSeeAlso to register classes in the JaxbContext
// or add them to the context directly
public class Child {
private int age;
#XmlElement(name = "isdancing")
private boolean isDancing;
#XmlElement(name = "issoccerplayer")
private boolean isSoccerPlayer;
// Getter and setter for all fields
}
#XmlType(name = "boy") // can be omitted if default value matches with the default value
public class Boy extends Child {
}
#XmlType(name = "girl")
public class Girl extends Child {
}
Complete selfcontained example:
package jaxb;
import java.io.File;
import java.io.StringReader;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlType;
public class Inheritance {
public static void main(String[] args) throws JAXBException {
JAXBContext jaxbContext;
jaxbContext = JAXBContext.newInstance(Children.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
String x = "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\r\n"
+ " <children xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\r\n"
+ " <child xsi:type=\"girl\">\r\n" + " <age>12</age>\r\n"
+ " <isdancing>true</isdancing>\r\n" + " </child>\r\n"
+ " <child xsi:type=\"boy\">\r\n" + " <age>10</age>\r\n"
+ " <issoccerplayer>true</issoccerplayer>\r\n" + " </child>\r\n" + " </children>";
Children children = (Children) jaxbUnmarshaller.unmarshal(new StringReader(x));
System.out.println(children.getChildren().toString());
}
#XmlRootElement(name = "children")
#XmlAccessorType(XmlAccessType.FIELD)
public static class Children {
#XmlElement(name = "child")
private List<Child> childrenList;
public List<Child> getChildren() {
return childrenList;
}
public void setChildren(List<Child> children) {
this.childrenList = children;
}
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlSeeAlso({ Boy.class, Girl.class })
public static class Child {
private int age;
#XmlElement(name = "isdancing")
private boolean isDancing;
#XmlElement(name = "issoccerplayer")
private boolean isSoccerPlayer;
// Getter and setter for all fields
}
#XmlType(name = "boy")
public static class Boy extends Child {
}
#XmlType(name = "girl")
public static class Girl extends Child {
}
}
Clean solution for second approach (based on separate class-files):
public class App
{
public static void main(String[] args) throws JAXBException
{
JAXBContext jaxbContext = JAXBContext.newInstance(Children.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
File file = new File("C:/test2.xml");
Children children = (Children) jaxbUnmarshaller.unmarshal(file);
for (Child c : children.getChildren()) {
if (c instanceof Boy) {
System.out.println(((Boy)c).toString());
} else if (c instanceof Girl){
System.out.println(((Girl)c).toString());
}
}
}
}
Children.java
#XmlRootElement(name="children")
#XmlAccessorType(XmlAccessType.FIELD)
public class Children {
#XmlElement(name="child")
private List<Child> childrenList;
public List<Child> getChildren() { return childrenList; }
public void setChildren(List<Child> children) {this.childrenList = children;}
#Override
public String toString() { return ReflectionToStringBuilder.toString(this); }
}
Boy.java
#XmlType(name="boy")
public class Boy extends Child {
#XmlElement(name = "issoccerplayer")
private boolean isSoccerPlayer;
public boolean isSoccerPlayer() { return isSoccerPlayer; }
public void setSoccerPlayer(boolean isSoccerPlayer) { this.isSoccerPlayer = isSoccerPlayer; }
#Override
public String toString() { return ReflectionToStringBuilder.toString(this); }
}
Girl.java
#XmlType(name="girl")
public class Girl extends Child {
#XmlElement(name = "isdancing")
private boolean isDancing;
public boolean isDancing() { return isDancing; }
public void setDancing(boolean isDancing) { this.isDancing = isDancing; }
#Override
public String toString() { return ReflectionToStringBuilder.toString(this); }
}
Child.java
#XmlAccessorType(XmlAccessType.FIELD)
#XmlSeeAlso({ Boy.class, Girl.class })
public abstract class Child {
private int age;
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}
The output should be:
de.home.myproject.XmlTest.model.Girl#12edcd21[isDancing=true,age=12]
de.home.myproject.XmlTest.model.Boy#27bc2616[isSoccerPlayer=true,age=10]
I am trying to deserialize xml string into object but ran across strange problem. Everything is being serialized as intended, however when deserializing field which originates from parent class always returns null.
MyNffgDescriptor is a class which contain VNFTypeReader as attribute. All fields are deserialized correctly, VNFTypeReader ones as well except the name which is passed from parent class (NamedEntityReaderImpl) and it returns null.
Parent class
package it.polito.dp2.NFV.sol3.client1.implementations;
import java.io.Serializable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import it.polito.dp2.NFV.NamedEntityReader;
#XmlAccessorType(XmlAccessType.NONE)
public class NamedEntityReaderImpl implements NamedEntityReader, Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private String entityName;
public NamedEntityReaderImpl() {
this.entityName = "";
}
public NamedEntityReaderImpl(String name) {
this.entityName = name;
}
#Override
public String getName() {
return this.entityName;
}
}
Child class
package it.polito.dp2.NFV.sol3.client1.implementations;
import java.io.Serializable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import it.polito.dp2.NFV.VNFTypeReader;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class VNFTypeReaderImpl extends NamedEntityReaderImpl implements Serializable,VNFTypeReader {
/**
*
*/
private static final long serialVersionUID = 1L;
#XmlElement
private int requiredMemory;
#XmlElement
private int requiredStorage;
#XmlElement
private it.polito.dp2.NFV.FunctionalType funcType;
public VNFTypeReaderImpl() {
super(null);
this.requiredMemory = 0;
this.requiredStorage = 0;
this.funcType = null;
}
public VNFTypeReaderImpl(VNFTypeReader reader) {
super(reader.getName());
this.requiredMemory = reader.getRequiredMemory();
this.requiredStorage = reader.getRequiredStorage();
this.funcType = reader.getFunctionalType();
}
#Override
public int getRequiredMemory() {
return this.requiredMemory;
}
#Override
public int getRequiredStorage() {
return this.requiredStorage;
}
#Override
public it.polito.dp2.NFV.FunctionalType getFunctionalType() {
return this.funcType;
}
#Override
#XmlElement(name="name", required=true)
public String getName() {
return super.getName();
}
}
This is the example of xml string I am trying to deserialize:
<?xml version="1.0" encoding="UTF-8"?>
<myNffgDescriptor>
<nodeList id="0">
<funcReader>
<requiredMemory>620</requiredMemory>
<requiredStorage>100</requiredStorage>
<funcType>WEB_SERVER</funcType>
<name>WEBSERVERt</name>
</funcReader>
<hostName>H3</hostName>
<linksList source="0" destination="10" />
<linksList source="0" destination="11" />
<linksList source="0" destination="8" />
</nodeList>
</myNffgDescriptor>
Place where unmarshalling occurs:
jaxbContext = JAXBContext.newInstance(MyNffgDescriptor.class);
unmarshaller = jaxbContext.createUnmarshaller();
StringReader reader = new StringReader(nffg);
MyNffgDescriptor nffgDesc = (MyNffgDescriptor) unmarshaller.unmarshal(reader);
Change XmlAccessType.NONE to XmlAccessType.FIELD on parent
class NamedEntityReaderImpl.
Also you could just add on class NamedEntityReaderImpl an annotation above the field:
#XmlElement(name= "name")
private String entityName;
Do either changes (or both) and it would work
I've been experimenting with JAXB tutorials and have managed to get code working that generates an XML file from a Java object and then is able to use the XML to generate a Java object. At the moment it reads multiple instances of the same class to create an XML file similar to the one below
<Car>
<regplate>TR54</regplate>
<colour>red</colour>
<energyrating>5</energyrating>
</Car>
<Car>
<regplate>BN04 THY</regplate>
<colour>yellow</colour>
<energyrating>3</energyrating>
</Car>
<Car>
<regplate>BN05 THY</regplate>
<colour>yellow</colour>
<energyrating>5</energyrating>
</Car>
I would like to be able to use the JAXB technology to work with subclasses. For example: Say I have a Car, Van and Bicycle objects that are subclasses of Vehicle. Is it possible for me to manipulate my JAXB class to write an XML file that would produce something similar to this? I have provided the code I am working with below.
<Vehicle>
<Car>
<regplate>TR54</regplate>
<colour>red</colour>
<energyrating>5</energyrating>
</Car>
<Van>
<regplate>MN05 RFD</regplate>
<colour>red</colour>
<energyrating>5</energyrating>
</Van>
<Car>
<regplate>ZX54 UJK</regplate>
<colour>red</colour>
<energyrating>1</energyrating>
</Car>
</Vehicle>
Main Class
package basictransport2;
public class Main
{
public static void main(String[] args)
{
JAXB parser = new JAXB();
parser.marshall();
//parser.unmarshallList();
}
}
Vehicle Class
package basictransport2;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
//#XmlRootElement(name = "Vehicle")
public class Vehicle
{
private int ownerId;
public Vehicle(int ownerId)
{
this.setOwnerId(ownerId);
}
//#XmlElement (name = "Owner ID")
public int getOwnerId()
{
return ownerId;
}
public void setOwnerId(int ownerId)
{
this.ownerId = ownerId;
}
public int getEnergyRating()
{
return (Integer) null;
}
public String getColour()
{
return null;
}
public String getRegPlate()
{
return null;
}
}
Car Class
package basictransport2;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
//#XmlRootElement(name = "Car")
public class Car extends Vehicle
{
private String regPlate;
private int energyRating;
private String colour;
public Car(String regPlate, int energyRating, String colour, int ownerId)
{
super(ownerId);
this.regPlate = regPlate;
this.energyRating = energyRating;
this.colour = colour;
}
public Car(int ownerId)
{
super(ownerId);
}
//#XmlElement (name = "Registration")
public String getRegPlate()
{
return regPlate;
}
public void setRegPlate(String regPlate)
{
if(this.regPlate == null)
{
this.regPlate = regPlate;
}
}
//#XmlElement (name = "Energy Rating")
public int getEnergyRating()
{
return energyRating;
}
public void setEnergyRating(int energyRating)
{
this.energyRating = energyRating;
}
//#XmlElement (name = "Colour")
public String getColour()
{
return colour;
}
public void setColour(String colour)
{
this.colour = colour;
}
}
JAXB Class
package basictransport2;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
public class JAXB
{
public void marshall()
{
try
{
List<Vehicle> vehicleList = new ArrayList<Vehicle>();
vehicleList.add(new Car("SG09 TYH", 4, "Yellow", 1));
vehicleList.add(new Car("XX09 VVV", 3, "Red", 2));
vehicleList.add(new Car("BL09 TYZ", 4, "Blue", 3));
Garage listOfVehicles = new Garage();
listOfVehicles.setListOfVehicles(vehicleList);
JAXBContext context = JAXBContext.newInstance(Garage.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(listOfVehicles, System.out);
marshaller.marshal(listOfVehicles, new File("src\\data\\listcar.xml"));
}
catch(Exception e)
{
System.out.println(e.getMessage());
}
}
public void unmarshall()
{
try
{
JAXBContext context = JAXBContext.newInstance(Garage.class);
Unmarshaller unmarhsaller = context.createUnmarshaller();
Garage listOfVehicles = (Garage)unmarhsaller.unmarshal(new File("src\\data\\listcar.xml"));
System.out.println("List Car information");
for(Vehicle vehicle : listOfVehicles.getListOfVehicles())
{
System.out.println("Reg Plate: " + vehicle.getRegPlate());
System.out.println("Energy Rating: " + vehicle.getEnergyRating());
System.out.println("Colour: " + vehicle.getColour());
System.out.println("================");
}
}
catch (Exception e)
{
System.out.println(e.getMessage());
}
}
}
List class
package basictransport2;
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.XmlElement;
import javax.xml.bind.annotation.XmlElements;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlType;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name="Vehicle")
public class Garage
{
#XmlElements
({
#XmlElement(name = "Car", type = Car.class, required = false)
})
private List<Vehicle> vehicleCollection = new ArrayList<Vehicle>();
public List<Vehicle> getListOfVehicles()
{
return vehicleCollection;
}
public void setListOfVehicles(List<Vehicle> listOfVehicles)
{
this.vehicleCollection = listOfVehicles;
}
}
Thanks everyone for your input. I used feedback from all your answers but ultimately it was a combination of them that worked which is why I created a seperate answer for anyone who may have this problem in the future.
To get this to work I had to ensure that all getter methods within the super and sub classes being marhsalled/unmarshalled were annotated with #XmlElement. This would determine the XML tag for the corresponding variable.
#XmlElement (name = "OwnerID")
public int getOwnerId()
{
return ownerId;
}
The superclass had to be annotated with #XmlSeeAlso to bind the subclasses to it. i.e In my code RoadVehicle was the superclass and both the Car and Van classes extended it.
#XmlSeeAlso({Car.class, Van.class})
public class Vehicle
{
With the super and subclasses now annotated the only other class that required annotations was the list class (Garage in my code). The changes here would determine what the XML tags were populated with.
The root XML tag was set by applying the #XmlRootElement annotation to the top of the class. i.e. "Vehicle" would be the root XML tag in my example.
#XmlRootElement(name = "Vehicle")
public class Garage
{
Finally an #XmlElements list had to be declared with an #XmlElements annotation for each sub class that required an XML tag with the name supplying the name of the XML tag. This list had to be declared above the getter method for the collection.
#XmlElements
({
#XmlElement(name = "Car", type = Car.class, required = false),
#XmlElement(name = "Van", type = Van.class, required = false)
})
public List<Vehicle> getListOfVehicles()
{
return vehicleCollection;
}
you are on right track. May something below will help
#XmlRootElement(name = "car")
public class Car extends BasicType{
}
#XmlRootElement(name = "van")
public class Van extends BasicType{
}
#XmlRootElement(name = "vehicle")
public class Vehicle {
List<BasicType> basicType;
}
The simplest solution is to have different subclasses for cars and vans, even it they don't add anything to the base classes. Then, the root element class contains a list of the base class, with element QNames identifying the actual class.
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Vehicle")
public class Vehicle {
#XmlElements({
#XmlElement(name = "Car", type = Car.class, required = false),
#XmlElement(name = "Van", type = Van.class, required = false)
})
protected List carOrVan;
public List getCarOrVan() {
if (carOrVan == null) {
carOrVan = new ArrayList();
}
return this.carOrVan;
}
}
Here's the base class and the subclasses:
public class Basic {
private String regplate;
private String color;
private String energyrating;
public String getRegplate(){ return regplate; }
public void setRegplate( String v ){ regplate = v; }
public String getColor(){ return color; }
public void setColor( String v ){ color = v; }
public String getEnergyrating(){ return energyrating; }
public void setEnergyrating( String v ){ energyrating = v; }
}
public class Car extends Basic {}
public class Van extends Basic {}
This will go smoothly if cars and vans develop into distinct subclasses.
I am using JAXB to unmarshal an XML file.
All I know about the XML file is that it is valid XML.
How then am I supposed to specify a class and/or package to newInstance?
JAXBContext jaxbContext = JAXBContext.newInstance(??????);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
Object o = (Object) unmarshaller.unmarshal(myFile);
I did not see anything in the docs that address this issue.
You need to tell JaxB what class to unmarshall to so that it can use the annotations in the class to resolve the hierarchy of the xml. You will need to have a class that is also annotated with something like #XmlRootElement. If you want to parse arbitrary xml you will probably need to do something with a DocumentBuilder or xpath.
See this artical for more info.
http://blog.bdoughan.com/2012/11/creating-generic-list-wrapper-in-jaxb.html
I have used something like this to convert arbitrary xml to a class. The any field will actually be a list of org.w3c.dom.Element in which you can get information from.
http://docs.oracle.com/javase/7/docs/api/org/w3c/dom/Element.html
#XmlRootElement
class Wrapper {
/**
* Everything else
*/
#Transient
#XmlAnyElement(lax = true)
private List<Element> any;
public List<Element> getAny() {
return any;
}
}
In newInstance you must add the class root element that map your xml... below an example
Here an example ..
public static void main(String[] args) throws JAXBException {
final JAXBContext context = JAXBContext.newInstance(Vehicals.class);
final Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
final Vehicals vehicals = new Vehicals();
List<Car> cars = new ArrayList<Car>();
Car c = new Car();
c.setName("Mercedes");
cars.add(c);
c = new Car();
c.setName("BMW");
cars.add(c);
vehicals.setCar(cars);
m.marshal(vehicals, System.out);
}
Vehicals.java
import java.util.List;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Vehicals {
private List<Car> Car;
public List<Car> getCar() {
return Car;
}
public void setCar(List<Car> cars) {
this.Car = cars;
}
}
Car.java
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
#XmlRootElement
public class Car {
#XmlTransient
private Long id;
private String name;
#XmlTransient
private String code;
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;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
output.xml
<Vehicle>
<Car>
<name>Mercedes</name>
</Car>
<Car>
<name>BMW</name>
</Car>
</Vehicle>
For the Unmarshal is the same thing. In my case i added Vehicals as parameter in newInstance method.