NULL values in JAXB unmarshalling - java

HI I want to fetch the value of nested xml using un-marshaling using maven dependency but the final output is returning me null values.I have used the 3 packages in maven project and vehicle.xml contains the values of car after fetching the values I have to insert them into access Database
MY XML file---> Vehicle.xml
<?xml version="1.0" encoding="UTF-8"?>
<Vehicle>
<Car>
<manufacturer>Maruti</manufacturer>
<cost>675000</cost>
<name>Ciaz</name>
<fueType>Petrol</fueType>
<driverTye>Manual</driverTye>
</Car>
<Car>
<manufacturer>Maruti</manufacturer>
<cost>575000</cost>
<name>Dezire</name>
<fueType>Petrol</fueType>
<driverTye>Manual</driverTye>
</Car>
</Vehicle>
POJO CLASS
Vehicle.java
package jaxb;
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="Vehicle")
public class Vehicle {
#XmlElement
private List<Car> car;
public List<Car> getCar() {
return car;
}
/*
* public Vehicle(List<Car> car) { super(); this.car = car; }
*/
#Override
public String toString() {
return "Vehicle[ Car="+car+"]";
}
}
Car.java (This is child POJO)
package jaxb;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="Car")
public class Car {
private String manufacturer;
private String name;
private String driverType;
private String fuelType;
private int cost;
#XmlElement
public String getManufacturer() {
return manufacturer;
}
public void setManufacturer(String manufacturer) {
this.manufacturer = manufacturer;
}
#XmlElement
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#XmlElement
public String getDriverType() {
return driverType;
}
public void setDriverType(String driverType) {
this.driverType = driverType;
}
#XmlElement
public String getFuelType() {
return fuelType;
}
public void setFuelType(String fuelType) {
this.fuelType = fuelType;
}
#XmlElement
public int getCost() {
return cost;
}
public void setCost(int cost) {
this.cost = cost;
}
#Override
public String toString() {
return "Car [name=" + name + ", fuelType=" + fuelType + ", cost=" + cost+",driverType="+driverType +"]";
}
VehicleJxb.java
This file contains the implementation of our unmarshalling method
package jaxb;
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
public class VehicleJxb {
public void unmarhalling() {
try {
JAXBContext jaxbContext = JAXBContext.newInstance(Vehicle.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Vehicle vehicle = (Vehicle) jaxbUnmarshaller.unmarshal(new File("src\\main\\java\\Data\\Vehicle.xml"));
System.out.println(vehicle);
} catch (JAXBException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
App.java
package com.project.XMLDB;
import jaxb.*;
public class App
{
public static void main( String[] args )
{
VehicleJxb obj= new VehicleJxb();
obj.unmarhalling();
}
}
My Output is coming
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.sun.xml.bind.v2.runtime.reflect.opt.Injector (file:/C:/Users/Shivam%20Sharma/.m2/repository/com/sun/xml/bind/jaxb-impl/2.2.11/jaxb-impl-2.2.11.jar) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int)
WARNING: Please consider reporting this to the maintainers of com.sun.xml.bind.v2.runtime.reflect.opt.Injector
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
Vehicle[ Car=null]
I want to get the values as final output is returned null

Unmarshaller is case sensitive. In your pojo, you have variable name car but in XML you Car. Change it to the following and it will work.
<?xml version="1.0" encoding="UTF-8"?>
<Vehicle>
<car>
<manufacturer>Maruti</manufacturer>
<cost>675000</cost>
<name>Ciaz</name>
<fueType>Petrol</fueType>
<driverTye>Manual</driverTye>
</car>
<car>
<manufacturer>Maruti</manufacturer>
<cost>575000</cost>
<name>Dezire</name>
<fueType>Petrol</fueType>
<driverTye>Manual</driverTye>
</car>
</Vehicle>
Or you need to mention that explicitly.
#XmlElement(name = "Car")
private List<Car> car;

Related

How to read xsi:type with java annotations

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]

unmarshaling always shows 0 and null

I think the best way is to copy my code so you can understand it.
Here are my POJO classes:
import javax.xml.bind.annotation.XmlAttribute;
public class Product {
private String name;
public Product() {
}
public Product(String name) {
this.name = name;
}
#XmlAttribute(name = "productName")
public String getProduct() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="ShoppingCart")
public class Cart {
private List<Product> productList = new ArrayList<>();
private long CartIdentifier;
public Cart() {
}
public Cart(long id) {
this.CartIdentifier=id;
}
#XmlAttribute(name="CartIdentifier")
public long getId() {
return CartIdentifier;
}
public void setType(long id) {
this.CartIdentifier=id;
}
public Cart(List<Product> list) {
this.productList = list;
}
public void addElement(Product element) {
this.productList.add(element);
}
#XmlElement(name = "CurrentProducts")
public List<Product> getCart() {
return this.productList;
}
public void setCart(List<Product> cart) {
this.productList = cart;
}
}
import java.util.ArrayList;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "CashRegister")
public class Register {
#XmlElement(name = "ShoppingCart")
private ArrayList<Cart> listCart;
long CartIdentifier;
public Register() {
}
public Register(long id) {
this.CartIdentifier=id;
}
public long getId() {
return CartIdentifier;
}
public void addElementforBonus(Cart element) {
listCart.add(element);
}
public void setType(long id) {
this. CartIdentifier=id;
}
public void setList(ArrayList<Cart> list) {
this.listCart = list;
}
public ArrayList<Cart> getCartList() {
return listCart;
}
}
So those are my POJO classes and in main they create perfect XML, but when it comes to converting from XML to normal output something goes wrong. Can someone tell me where am I making mistake and why is my output than alway 0 and null?
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
public class Main {
public static void main(String[] args) throws JAXBException, IOException {
ArrayList<Cart> list = new ArrayList<Cart>();
Register store = new Register(1);
store.setType(1);
store.setList(list);
Product first = new Product("Burger");
Product second = new Product("Banana");
Cart cart1 = new Cart(1);
cart1.setType(1);
cart1.addElement(first);
cart1.addElement(second);
store.getCartList().add(cart1);
// create JAXB context and instantiate marshaller
JAXBContext context = JAXBContext.newInstance(Register.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
// Write to System.out
m.marshal(store, System.out);
m.marshal(store, new File("mrs.xml"));
System.out.println("Output from our XML File: ");
Unmarshaller um = context.createUnmarshaller();
Register reg = (Register) um.unmarshal(new FileReader("mrs.xml"));
ArrayList<Cart> list1 = reg.getCartList();
for(Cart x:reg.getCartList()) {
System.out.println(x.getId());
}
for (Cart cart : list1) {
System.out.println("Cart: " + cart.getId());
for(Product product : cart.getCart()) {
System.out.println("Product: " + product.getProduct());
}
}
}
}
So here is my output:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<CashRegister>
<ShoppingCart CartIdentifier="1">
<CurrentProducts productName="Burger"/>
<CurrentProducts productName="Banana"/>
</ShoppingCart>
</CashRegister>
Output from our XML File:
0
Cart: 0
Product: null
Product: null
There is one cart with two products but not with 0 and null.
No, my question isn't duplicate. I don't want to have that as Element or show the name of that element in my xml. My xml looks perfect but i can't unmarshal it
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<CashRegister>
<ShoppingCart CartIdentifier="1">
<CurrentProducts productName="Burger"/>
<CurrentProducts productName="Banana"/>
</ShoppingCart>
</CashRegister>
Output from our XML File:
Exception in thread "main" java.lang.ClassCastException: bonusxml.Register cannot be cast to bonusxml.Register1
at bonusxml.Main.main(Main.java:45)
If you are hell bent on preserving your XML structure, you will have to pay a price for that. Below, are classes that can get you de-serializing the XML you have created. But, you will end up using two different set of classes for serializing and de-serializing.
Cart.java
#XmlRootElement(name="ShoppingCart")
public class Cart1 {
#XmlElement(name = "CurrentProducts")
private List<Product1> productList = new ArrayList<>();
#XmlAttribute(name="CartIdentifier")
private long CartIdentifier;
public Cart1() {}
public Cart1(long id) {
this.CartIdentifier=id;
}
// setters & getters
}
Product.java
public class Product1 {
#XmlAttribute(name = "productName")
private String name;
public Product1() {
}
public Product1(String name) {
this.name = name;
}
// setters & getters
}
Register.java
#XmlRootElement(name = "CashRegister")
public class Register1 {
#XmlElement(name = "ShoppingCart")
private ArrayList<Cart1> listCart;
long CartIdentifier;
public Register1() {
}
public Register1(long id) {
this.CartIdentifier=id;
}
//setters & getters
}

How to Use Jaxb for nested POJO class to generate XML model in Java

I'm using JAXB to create an xml data model and it seems to be working really well for non-complex XML model but as soon as I get into a slightly complex XML, JAXB doesn't seem to work as well.
This is the XML-generation I would like to acheive:
<?xml version="1.0" encoding="UTF-8"?>
<SampleRoot xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="Sample.xsd">
<Name>Sample Name</Name>
<Description>This is a description</Description>
<Graph>Graph test</Graph>
<VerifyAttr>
<Data>id</Data>
<Value>32</Value>
</VerifyAttr>
<VeirfyXpath>
<MemXpath>/Root/Name</MemXpath>
<Value>Mosawi</Value>
</<VeirfyXpath>>
</SampleRoot>
This is the Nested Pojo class I created (should I not create a nested pojo? Perhaps separate them out?):
package com.sample.model;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="SampleRoot")
public class Sample {
private String name;
private String description;
private String graph;
public String getName() {
return name;
}
#XmlElement
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
#XmlElement
public void setDescription(String description) {
this.description = description;
}
public String getGraph() {
return graph;
}
#XmlElement
public void setGraph(String graph) {
this.graph = graph;
}
#XmlAccessorType(XmlAccessType.FIELD)
public class VerifyAttr {
private String data;
private String value;
public String getData() {
return data;
}
#XmlElement
public void setData(String data) {
this.data = data;
}
public String getValue() {
return value;
}
#XmlElement
public void setValue(String value) {
this.value = value;
}
}
#XmlAccessorType(XmlAccessType.FIELD)
public class VerifyXpath {
private String memXpath;
private String value;
public String getMemXpath() {
return memXpath;
}
#XmlElement
public void setMemXpath(String memXpath) {
this.memXpath = memXpath;
}
public String getValue() {
return value;
}
#XmlElement
public void setValue(String value) {
this.value = value;
}
}
}
And here is the demo to exercise the pojo model:
public static void main(String[] args) {
Sample sample = new Sample();
sample.setName("Name");
sample.setDescription("This is a description");
sample.setGraph("Graph Test");
VerifyAttr va = sample.new VerifyAttr();
va.setData("id");
va.setValue("32");
VerifyXpath vx = sample.new VerifyXpath();
vx.setMemXpath("/Root/Name");
vx.setValue("Mosawi");
try {
JAXBContext jaxbContext = JAXBContext.newInstance(Sample.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(sample, new File("Sample.xml") );
jaxbMarshaller.marshal(sample, System.out);
} catch(Exception e) {
e.printStackTrace();
}
}
The problem I have is that the full XML as defined above is not generated. This is what is generated:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<SampleRoot>
<description>This is a description</description>
<graph>Graph Test</graph>
<name>Name</name>
</SampleRoot>
What am I doing wrong? Why am I not getting the model XML as I should above? The problem is with demo main?
Okay so I was able to figure this out by reading some JaxB documentation. I've decoupled the POJOs into 3 classes and adjusted the Jaxb annotations:
Sample.java
VerifyAttr.java
VerifyXpath.java
VerifyAttr.java
#XmlRootElement
#XmlAccessorType(XmlAccessType.PROPERTY)
#XmlType(propOrder={"dataAttr","valueAttr"})
public class VerifyAttr {
//...
}
VerifyXpath.java
#XmlRootElement
#XmlAccessorType(XmlAccessType.PROPERTY)
#XmlType(propOrder={"dataXpath","valueXpath"})
public class VerifyAttr {
//...
}
and a top level for root node:
Sample.java
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class VerifyAttr {
#XmlElement(name="VerifyAttr", type=VerifyAttr.class)
private ArrayList<VerifyAttr> va;
#XmlElement(name="VerifyXpath", type=VerifyXpath.class)
private ArrayList<VerifyXpath> vx;
// some other fields
//..getter and setters
}
Hopefully this helps someone else new to Jaxb

Marshalling/Unmarshalling Java superclass and subclasses using JAXB

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.

JAXB - Reference to id with interface,abstract and list<abstract> [duplicate]

I'm wondering if it's possible to annotate my classes so that the first time the marshaller encounters an object, it generates an XML element of the appropriate type, but any subsequent reference to this object by anything else will have an XML IDREF entry created?
You can leverage the concept of JAXB's XmlAdapter to do something like the following:
input.xml
The following is the XML document I will use for this example. The 3rd phone-number entry is a reference to the 1st phone-number entry, and the 5th phone-number entry is a reference to the 4th.:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer>
<phone-number id="A">
<number>555-AAAA</number>
</phone-number>
<phone-number id="B">
<number>555-BBBB</number>
</phone-number>
<phone-number id="A"/>
<phone-number xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="work-phone-number" id="W">
<number>555-WORK</number>
<extension>1234</extension>
</phone-number>
<phone-number xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="work-phone-number" id="W"/>
</customer>
Customer
The customer class maintains a collection of PhoneNumber objects. The same instance of PhoneNumber may appear multiple times in the collection.
package forum7587095;
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Customer {
private List<PhoneNumber> phoneNumbers;
#XmlElement(name="phone-number")
public List<PhoneNumber> getPhoneNumbers() {
return phoneNumbers;
}
public void setPhoneNumbers(List<PhoneNumber> phoneNumbers) {
this.phoneNumbers = phoneNumbers;
}
}
PhoneNumber
This is a class that can either appear in the document itself or as a reference. This will be handled using an XmlAdapter. An XmlAdapter is configured using the #XmlJavaTypeAdapter annotation. Since we have specified this adapter at the type/class level it will apply to all properties referencing the PhoneNumber class:
package forum7587095;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlJavaTypeAdapter(PhoneNumberAdapter.class)
public class PhoneNumber {
private String id;
private String number;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
#Override
public boolean equals(Object arg0) {
if(null == arg0 || arg0.getClass() != this.getClass()) {
return false;
}
PhoneNumber test = (PhoneNumber) arg0;
if(!equals(id, test.getId())) {
return false;
}
return equals(number, test.getNumber());
}
protected boolean equals(String control, String test) {
if(null == control) {
return null == test;
} else {
return control.equals(test);
}
}
#Override
public int hashCode() {
return id.hashCode();
}
}
WorkPhoneNumber
Based on your comment I have added a subclass of PhoneNumber.
package forum7587095;
public class WorkPhoneNumber extends PhoneNumber {
private String extension;
public String getExtension() {
return extension;
}
public void setExtension(String extension) {
this.extension = extension;
}
#Override
public boolean equals(Object arg0) {
if(!super.equals(arg0)) {
return false;
}
return equals(extension, ((WorkPhoneNumber) arg0).getExtension());
}
}
PhoneNumberAdapter
Below is the implementation of the XmlAdapter. Note that we must maintain if the PhoneNumber object has been seen before. If it has we only populate the id portion of the AdaptedPhoneNumber object.
package forum7587095;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class PhoneNumberAdapter extends XmlAdapter<PhoneNumberAdapter.AdaptedPhoneNumber, PhoneNumber>{
private List<PhoneNumber> phoneNumberList = new ArrayList<PhoneNumber>();
private Map<String, PhoneNumber> phoneNumberMap = new HashMap<String, PhoneNumber>();
#XmlSeeAlso(AdaptedWorkPhoneNumber.class)
#XmlType(name="phone-number")
public static class AdaptedPhoneNumber {
#XmlAttribute public String id;
public String number;
public AdaptedPhoneNumber() {
}
public AdaptedPhoneNumber(PhoneNumber phoneNumber) {
id = phoneNumber.getId();
number = phoneNumber.getNumber();
}
public PhoneNumber getPhoneNumber() {
PhoneNumber phoneNumber = new PhoneNumber();
phoneNumber.setId(id);
phoneNumber.setNumber(number);
return phoneNumber;
}
}
#XmlType(name="work-phone-number")
public static class AdaptedWorkPhoneNumber extends AdaptedPhoneNumber {
public String extension;
public AdaptedWorkPhoneNumber() {
}
public AdaptedWorkPhoneNumber(WorkPhoneNumber workPhoneNumber) {
super(workPhoneNumber);
extension = workPhoneNumber.getExtension();
}
#Override
public WorkPhoneNumber getPhoneNumber() {
WorkPhoneNumber phoneNumber = new WorkPhoneNumber();
phoneNumber.setId(id);
phoneNumber.setNumber(number);
phoneNumber.setExtension(extension);
return phoneNumber;
}
}
#Override
public AdaptedPhoneNumber marshal(PhoneNumber phoneNumber) throws Exception {
AdaptedPhoneNumber adaptedPhoneNumber;
if(phoneNumberList.contains(phoneNumber)) {
if(phoneNumber instanceof WorkPhoneNumber) {
adaptedPhoneNumber = new AdaptedWorkPhoneNumber();
} else {
adaptedPhoneNumber = new AdaptedPhoneNumber();
}
adaptedPhoneNumber.id = phoneNumber.getId();
} else {
if(phoneNumber instanceof WorkPhoneNumber) {
adaptedPhoneNumber = new AdaptedWorkPhoneNumber((WorkPhoneNumber)phoneNumber);
} else {
adaptedPhoneNumber = new AdaptedPhoneNumber(phoneNumber);
}
phoneNumberList.add(phoneNumber);
}
return adaptedPhoneNumber;
}
#Override
public PhoneNumber unmarshal(AdaptedPhoneNumber adaptedPhoneNumber) throws Exception {
PhoneNumber phoneNumber = phoneNumberMap.get(adaptedPhoneNumber.id);
if(null != phoneNumber) {
return phoneNumber;
}
phoneNumber = adaptedPhoneNumber.getPhoneNumber();
phoneNumberMap.put(phoneNumber.getId(), phoneNumber);
return phoneNumber;
}
}
Demo
To ensure the same instance of the XmlAdapter is used for the entire marshal and unmarshal operations we must specifically set an instance of the XmlAdapter on both the Marshaller and Unmarshaller:
package forum7587095;
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(Customer.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setAdapter(new PhoneNumberAdapter());
File xml = new File("src/forum7587095/input.xml");
Customer customer = (Customer) unmarshaller.unmarshal(xml);
System.out.println(customer.getPhoneNumbers().get(0) == customer.getPhoneNumbers().get(2));
System.out.println(customer.getPhoneNumbers().get(3) == customer.getPhoneNumbers().get(4));
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setAdapter(new PhoneNumberAdapter());
marshaller.marshal(customer, System.out);
}
}
Output
true
true
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer>
<phone-number id="A">
<number>555-AAAA</number>
</phone-number>
<phone-number id="B">
<number>555-BBBB</number>
</phone-number>
<phone-number id="A"/>
<phone-number xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="work-phone-number" id="W">
<number>555-WORK</number>
<extension>1234</extension>
</phone-number>
<phone-number xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="work-phone-number" id="W"/>
</customer>
For More Information
http://blog.bdoughan.com/2011/09/mixing-nesting-and-references-with.html
http://blog.bdoughan.com/2010/10/jaxb-and-shared-references-xmlid-and.html
http://blog.bdoughan.com/search/label/XmlAdapter
http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-xsitype.html
http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-substitution.html

Categories

Resources