JAXB: unmarshal diferent xml to same object - java

I have 3 input XML that have, pretty much, the same elements and attributes, in fact, they represent the same thing, so I want to marshall them to the same object, something like this:
Request One:
<?xml version="1.0" encoding="UTF-8"?>
<RequestOne>
<id>123</id>
<name>foo</name>
</RequestOne>
Request Two:
<?xml version="1.0" encoding="UTF-8"?>
<RequestTwo>
<id>123</id>
<value>val</value>
</RequestTwo>
Request Three:
<?xml version="1.0" encoding="UTF-8"?>
<RequestThree>
<name>foo</name>
<value>val</value>
</RequestThree>
Desired Object (something like):
#XmlRootElement
public class Resource{
#XmlElement
private String id;
#XmlElement
private String name;
#XmlElement
private String value;
//(...) more code
}
But I can't use multiple RootElement annotations to ask JAXB to unmarshall all of the 3 request to objects of the class Resource
Is there a way to do it? Or I must make the 3 sepparated classes?
Thanks for your help

Option 1
Unmarshal using the overloaded Generic unmarshal method :
public static class Base {
private String name ;
#XmlElement(name = "name")
public String getName() {
return name;
}
public Base setName(String name) {
this.name = name;
return this;
}
}
public static void main (String [] args) throws JAXBException {
JAXBContext jaxbContext = JAXBContext.newInstance(Base.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
JAXBElement<Base> basea = unmarshaller.unmarshal(new StreamSource(new StringReader("<RootA><name>nanana</name></RootA>")), Base.class);
System.out.println(basea.getValue().getName());
JAXBElement<Base> baseb = unmarshaller.unmarshal(new StreamSource(new StringReader("<RootB><name>nbnbnb</name></RootB>")), Base.class);
System.out.println(baseb.getValue().getName());
}
Option 2
You can always use Java's class subtyping capabilites ? JAXB does annotation scanning on parent class as well. This example works
public static class Base {
private String name ;
#XmlElement(name = "name")
public String getName() {
return name;
}
public Base setName(String name) {
this.name = name;
return this;
}
}
#XmlRootElement( name = "RootA")
public static class RootA extends Base{
}
#XmlRootElement( name = "RootB")
public static class RootB extends Base {
}
public static void main (String [] args) throws JAXBException {
JAXBContext jaxbContext = JAXBContext.newInstance(RootA.class,RootB.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
RootA rootA = (RootA)unmarshaller.unmarshal(new StringReader("<RootA><name>nanana</name></RootA>"));
System.out.println(rootA.getName());
RootB rootB = (RootB)unmarshaller.unmarshal(new StringReader("<RootB><name>nbnbnb</name></RootB>"));
System.out.println(rootB.getName());
}

Related

java JAXB save subclass when saving

I'm struggling saving all data from my class/subclass using JAXB.
I want to save all accounts from an observableList, but the problem is, the account class
public class Account{
private ObjectProperty<HosterObject> host;
....
}
contains an HosterObject which has 2 attributes:
publicName and privateName also have getter and setter.
#XmlRootElement(name = "hoster")
public class HosterObject {
private final StringProperty publicName;
private final StringProperty privateName;
public HosterObject(String publicName, String privateName){
this.publicName = new SimpleStringProperty(publicName);
this.privateName = new SimpleStringProperty(privateName);
}
#XmlElement(name = "publicName")
public StringProperty publicNameProperty(){
return publicName;
}
#XmlElement(name = "privateName")
public StringProperty privateNameProperty(){
return privateName;
}
How can I save the content from the Hosterobject as Element in the xml-file as well?
At the moment the xml file looks so:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<accounts>
<account>
<hoster/>
<password>123</password>
<status>unchecked</status>
<username>test</username>
</account>
</accounts>
But i should look kinda like this
...
<account>
<hoster>
<publicName>Name</publicName>
<privateName>private Name</privateName>
</hoster>
....
</account>
....
The code for saving:
public void saveAccountDataToFile(File file) {
try {
JAXBContext context = JAXBContext.newInstance(AccountListWrapper.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
// Wrapping our person data.
AccountListWrapper wrapper = new AccountListWrapper();
wrapper.setAccounts(accountData);
// Marshalling and saving XML to the file.
m.marshal(wrapper, file);
} catch (Exception e) {
}
}
Wrapper:
#XmlRootElement(name = "accounts")
public class AccountListWrapper {
private List<Account> accounts;
#XmlElement(name = "account")
public List<Account> getAccounts() {
return accounts;
}
public void setAccounts(List<Account> accounts) {
this.accounts = accounts;
}
}
Thanks in advance!
Add:
tag HosterObject by
#XmlRootElement(name = "hoster")
#XmlElement
for class and set method for HosterObject in Account.
public HosterObject (){}
public Account(){}
JAXB need default empty constructor.
If you want to add class to xml you must tag it and create always default public constructor. Remember to tag only class which are non abstract.

How to name complex objects in JAXB/JSON?

#XmlRootElement(name = "test")
public class MyDTO {
#XmlElement(name = "test2)
private MyObject meta;
}
Result:
{meta:{...}}
Problems:
I'd like to have some kind of "outer" tag named "test"
Why is the #XmlElement(name" attribute for meta not working?
my first post!
Indeed you can name your "outer" tag with #XmlRootElement. If you need another outer tag I am not sure how to realize this.
Your second concern might be because of the place where you put the #XmlElement. I placed it on my getter-method and it worked fine fore me.
For the JSON Output I used jersey-json-1.18.
The following works also for other complex types you could define instead of "String meta".
Here is the output I was able to produce:
As JSON
{"myId":"id1","myMeta":"text1"}
As XML
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<mytupel>
<myId>id1</myId>
<myMeta>text1</myMeta>
</mytupel>
This is my object:
#XmlRootElement(name = "mytupel")
public class Tupel {
// #XmlElement(name = ) does not work here - defined it on the getter method
private String id;
// #XmlElement(name = ) does not work here - defined it on the getter method
private String meta;
/**
* Needed for JAXB
*/
public Tupel() {
}
/**
* For Test purpose...
*/
public Tupel(String id, String text) {
super();
this.id = id;
this.meta = text;
}
#XmlElement(name = "myId")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#XmlElement(name = "myMeta")
public String getMeta() {
return meta;
}
public void setMeta(String meta) {
this.meta = meta;
}
/**
* For Test purpose...
*/
#Override
public String toString() {
return id + ": " + meta;
}
}
And here is my small class to produce the output XML files...
public class Main {
private static final String TUPEL_1_XML = "./tupel1.xml";
private static final String TUPEL_2_XML = "./tupel2.xml";
public static void main(String[] args) throws JAXBException, FileNotFoundException {
// init JAXB context/Marhsaller stuff
JAXBContext context = JAXBContext.newInstance(Tupel.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
Unmarshaller unmarshaller = context.createUnmarshaller();
// create some Datatypes
Tupel data1 = new Tupel("id1", "text1");
Tupel data2 = new Tupel("id2", "42");
// produce output
marshaller.marshal(data1, new File(TUPEL_1_XML));
marshaller.marshal(data2, new File(TUPEL_2_XML));
// read from produced output
Tupel data1FromXml = (Tupel) unmarshaller.unmarshal(new FileReader(TUPEL_1_XML));
Tupel data2FromXml = (Tupel) unmarshaller.unmarshal(new FileReader(TUPEL_2_XML));
System.out.println(data1FromXml.toString());
System.out.println(data2FromXml.toString());
System.out.println(marshalToJson(data1FromXml));
System.out.println(marshalToJson(data2FromXml));
}
public static String marshalToJson(Object o) throws JAXBException {
StringWriter writer = new StringWriter();
JAXBContext context = JSONJAXBContext.newInstance(o.getClass());
Marshaller m = context.createMarshaller();
JSONMarshaller marshaller = JSONJAXBContext.getJSONMarshaller(m, context);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshallToJSON(o, writer);
return writer.toString();
}
}
Hope this answers your question!
Cheers
Max

JAXB unmarshal mystery XML

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.

How to create model class for XML parsing which has iteration of element

I'm beginner JAVA programming. I'm making model class through XML parsing. I know there are some example such as : JAXB, xmapper etc..
I have some xml file which element has iteration. How to create model class for this xml ? Any help..
Let’s map some xml:
<root a="2.2">
<main>
<node1>123</node1>
<node2>123</node2>
</main>
<client value="1" use="true">
<C_node1>aaa</C_node1>
<C_node2>bbb</C_node2>
</client>
<client value="2" use="true">
<C_node1>aaa</C_node1>
<C_node2>bbb</C_node2>
</client>
<client value="3" use="true">
<C_node1>aaa</C_node1>
<C_node2>bbb</C_node2>
</client>
// ...
<client value="100" use="true">
<C_node1>aaa</C_node1>
<C_node2>bbb</C_node2>
</client>
<System>
<DebugFrame>0</DebugFrame>
</System>
</root>
I found http://docs.oracle.com/cd/E12840_01/wls/docs103/webserv/data_types.html. Is that what I want ?
Edited
Here is real code. I have some compile error. Java version is
java version "1.7.0_25"
Java(TM) SE Runtime Environment (build 1.7.0_25-b17)
Java HotSpot(TM) 64-Bit Server VM (build 23.25-b01, mixed mode)
Here is error message ;
com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 2 counts of IllegalAnnotationExceptions
Class has two properties of the same name "mode"
this problem is related to the following location:
at public java.lang.String xmlParserTest.RootTest.getMode()
at xmlParserTest.RootTest
this problem is related to the following location:
at private java.lang.String xmlParserTest.RootTest.mode
at xmlParserTest.RootTest
Class has two properties of the same name "inputFile"
this problem is related to the following location:
at public java.lang.String xmlParserTest.MainEntity.getInputFile()
at xmlParserTest.MainEntity
at private xmlParserTest.MainEntity xmlParserTest.RootTest.main
at xmlParserTest.RootTest
this problem is related to the following location:
at private java.lang.String xmlParserTest.MainEntity.inputFile
at xmlParserTest.MainEntity
at private xmlParserTest.MainEntity xmlParserTest.RootTest.main
at xmlParserTest.RootTest
: console.java
package xmlParserTest;
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
public class console {
public static void main(String[] args) {
try
{
JAXBContext jc = JAXBContext.newInstance(RootTest.class);
Unmarshaller u = jc.createUnmarshaller();
File f = new File("Testing.xml");
RootTest product = (RootTest) u.unmarshal(f);
System.out.println(product.getMode());
System.out.println(product.getMainEntity().getInputFile());
System.out.println(product.getMainEntity().getOutputFolder());
}catch (JAXBException e) {
e.printStackTrace();
}
}
}
: RootTest.java
package xmlParserTest;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "Function")
public class RootTest {
private MainEntity main;
private String mode;
#XmlElement(name="Main")
public MainEntity getMainEntity() {
return main;
}
public void setMainEntity(MainEntity _main) {
this.main = _main;
}
#XmlAttribute(name="mode")
public String getMode() {
return mode;
}
public void setMode(String _mode) {
this.mode = _mode;
}
public RootTest()
{
}
}
: MainEntity.java
package xmlParserTest;
import javax.xml.bind.annotation.XmlElement;
public class MainEntity {
private String inputFile;
private String inputType;
private String outputFolder;
private String outputType;
#XmlElement(name="InputFile")
public String getInputFile() {
return inputFile;
}
public void setInputFile(String _inputFile) {
this.inputFile = _inputFile;
}
public String getInputType() {
return inputType;
}
public void setInputType(String _type) {
this.inputType = _type;
}
#XmlElement(name="OutputFolder")
public String getOutputFolder() {
return outputFolder;
}
public void setOutputFolder(String _outputFolder) {
this.outputFolder = _outputFolder;
}
public String getOutputType() {
return outputType;
}
public void setOutputType(String _type) {
this.outputType = _type;
}
public MainEntity()
{
}
}
: Testing.xml
<?xml version="1.0" encoding="UTF-8"?>
<Function mode="Execute">
<Main>
<InputFile type="string">C:\DATA\test.txt</InputFile>
<OutputFolder type="string">C:\Test</OutputFolder>
</Main>
</Function>
If You wnat to use JAXB, first of all, You need to create main model, that looks like this:
#XmlRootElement(name = "root")
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
private MainEntity main;
#XmlElement(name = "client")
private List<ClientEntity> clients;
#XmlAttribute
private String a;
#XmlElement(name = "System")
private SystemEntity system;
//getters and setters for all fields
}
Then, specify entities:
public class MainEntity {
private String node1;
private String node2;
//getters and setters for all fields
}
#XmlAccessorType(XmlAccessType.FIELD)
public class ClientEntity {
#XmlElement(name = "C_node1")
private String C_node1;
#XmlElement(name = "C_node2")
private String C_node2;
#XmlAttribute
private Long value;
#XmlAttribute
private boolean use;
//getters and setters for all fields
}
#XmlAccessorType(XmlAccessType.FIELD)
public class SystemEntity {
#XmlElement(name = "DebugFrame")
private Long debugFrame;
//getters and setters for all fields
}
As You see, we use #XmlElement(name = "System") to set alias for field and #XmlAttribute to read field from attribute.
To unmarshall XML with JAXB, create context for your class model:
JAXBContext context = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
Root root = (Root) unmarshaller.unmarshal(file);
Don't forget: when You create model, You need to specify getters and setters for fields that You want to marshall/unmarshall and also specify non-argument constructor for class.
More about annotations You can find here:
http://www.techferry.com/articles/jaxb-annotations.html
Have fun!:)
With JAXB, you can create the required Java classes as below:
#XmlRootElement(name = "main")
public class Main {
String node1;
String node1;
//getters and setters for fields
}
#XmlRootElement(name = "client")
public class Client {
String node1;
String node1;
#XmlAttribute
private boolean use;
#XmlAttribute
private int value;
//getters and setters for fields
}
And then create a Root class to represent your XML file:
#XmlRootElement(name = "root")
public class Root {
//add a list of client instances
#XmlElement(name = "client")
private List<Client> clientList;
//add an instance of main
#XmlElement(name = "main")
private Main main;
// setters to set the values for main and clients
}
Now, with these lines of code you can create the XML representation for the Root class:
Root root = new Root();
root.setMain(CREATE_AND_SET_AN_INSTANCE_OF_MAIN);
root.setClientList(CREATE_AND_SET_A_LIST_OF_CLIENTS);
String filePath = "PATH_TO_SAVE_YOUR_FILE";
File file = new File(filePath);
JAXBContext jaxbContext = JAXBContext.newInstance(Root.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(alpha, file);
And you can use these lines of code to read the XML back to Java object:
String filePath = "XML_FILE_PATH";
File file = new File(filePath);
JAXBContext jaxbContext = JAXBContext.newInstance(Root.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Root root = (Root) jaxbUnmarshaller.unmarshal(file);
Ref: Java Architecture for XML Binding (JAXB)

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