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.
Related
I want to give custom names to the xml root element and to list elements.
But the annotations don't work.
#XmlRootElement(name = "test")
#XmlAccessorType(XmlAccessType.FIELD)
public class TestRsp {
#XmlElementWrapper(name = "persons")
#XmlElement(name = "pax")
private List<Person> persons;
}
public class Person {
private String name;
private String age;
}
Usage:
#RestController
public class MyServlet {
#RequestMapping("/test")
public TestRsp test() {
//...
return rsp;
}
}
Result:
<TestRsp> <!-- should be named "test" -->
<persons>
<persons> <!-- should be named "pax" -->
<name />
<age />
</persons>
<persons>
//...
</persons>
</persons>
</TestRsp>
So my xml annotations are not picked up. But why?
Try using #JsonProperty annotation.
It should look something like thit:
#JsonProperty("test")
EDIT 1:
Try annotating the getter of the fields that you want to change like so:
#XmlElement(name="someName")
EDIT 2:
#XmlRootElement(name="persons")
public class Root {
private List<String> someList;
#XmlElement(name="pax")
public List<String> getSomeList() {
return someList;
}
public void setSomeList(List<String> someList) {
this.someList = someList;
}
public Root(String numValue,List<String> someListValue) {
this();
this.number = numValue;
this.someList = someListValue;
}
/**
*
*/
public Root() {
// TODO Auto-generated constructor stub
}
}
Maybe this will provide:
<persons>
<pax>FOO</pax>
<pax>BAR</pax>
</persons>
EDIT 3:
Maybe if you want to make your solution works you need to add a file called jaxb.properties in with your model classes with the following entry:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
It turned out I have to use #JacksonXml* annotations instead:
#JacksonXmlRootElement(localName = "test")
public class TestRsp {
#JacksonXmlElementWrapper(localName = "persons")
#JacksonXmlProperty(localName = "pax")
#JsonProperty(name = "persons")
private List<Person> persons;
}
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());
}
I need to create XML file with following structure:
<Persons>
<Person ID="1">
<Name></Name>
...
</Person>
<Person ID="2">
<Name></Name>
...
</Person>
...
</Persons>
I have already tried all solutions on Stack Overflow, using *#XmlPath("/Person/#ID")*, *#XmlAttribute(name="ID")* in different ways and had no success. My code creates an attribute in root node or creates additional node before Person node. But attribute should be inside Person node
Method in Main.class:
public void savePersonDataToFile(File file) {
try {
JAXBContext context = JAXBContext
.newInstance(PersonListXMLWrapper.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
// Wrapping our person data.
PersonListXMLWrapper wrapper = new PersonListXMLWrapper();
wrapper.setPersons(personData);
// Marshalling and saving XML to the file.
m.marshal(wrapper, file);
// Save the file path to the registry.
setPersonFilePath(file);
} catch (Exception e) { // catches ANY exception
Alert alert = new Alert(AlertType.ERROR);
alert.setTitle("Error");
alert.setHeaderText("Cant save to file:\n" + file.getPath());
alert.setContentText("Ooops, there was an error!");
}
}
Person.class has variable declarations, methods for changing and getting them and constructor:
public class Person {
private final IntegerProperty ID;
private final StringProperty name;
private final StringProperty surname;
private final StringProperty prevSurname;
private final StringProperty patronymic;
...
PersonListXMLWrapper.class:
#XmlRootElement(name = "Persons")
public class PersonListXMLWrapper {
private List<Person> persons;
#XmlElement(name = "Person")
public List<Person> getPersons() {
return persons;
}
public void setPersons(List<Person> persons) {
this.persons = persons;
}
#XmlPath("/Person/#IDAtt")
public String IDAtt ="123";
#XmlAttribute(name="ID")
public String getID() {
return IDAtt;
}
public void setID(String ID) {
this.IDAtt = ID;
}
}
If you can use another XML parser, I would recommend the Scilca XML Progression for writing a simple XML file (verifying it).
import org.scilca.sxp.*; // The SXP package
Node rootNode = new XMLNode("Persons");
// First Persion
Node p1 = Node.constructNode("<Person Id=\"1\"?");
p1.add("Name").setValue("Jacob"); // Add a child element and set its value
p1.add("Habit").setValue("Eating");
rootNode.add(p1); // Add it to root node
// Second Person
Node p2 = Node.constructNode("<Person Id=\"2\">");
p1.add("Name").setValue("Jacks");
rootNode.add(p2);
Document XmlDocument = new Document(rootNode);
XmlWriter xw = XmlDocument.getWriter();
xw.saveXml("D:/file.txt"); // text file to see if the file is correct
Solution was extremely simple. I just had to add *#XmlAttribute(name="ID")* to method declaration in Person.class, instead of PersonListXMLWrapper.class:
public class Person {
private final StringProperty ID;
private final StringProperty name;
private final StringProperty surname;
private final StringProperty prevSurname;
private final StringProperty patronymic;
...
#XmlAttribute(name="ID")
public String getID() {
return ID.get();
}
public void setID(String ID) {
this.ID.set(ID);
}
public StringProperty IDProperty() {
return ID;
}
So i get the following structure:
<Persons>
<Person ID="1">
<name>Name1</name>
<surname></surname>
<prevSurname></prevSurname>
<patronymic></patronymic>
...
</Person>
<Person ID="2">
<name>Name2</name>
<surname></surname>
<prevSurname></prevSurname>
<patronymic></patronymic>
...
</Person>
...
</Persons>
I was going through the Blaise's Blog http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-substitution.html for Jaxb Inheritance using Substitution.
I want to implement the same but not to the root element. I am looking this type of XML as a output.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<configuration>
<customer>
<address>
<street>1 A Street</street>
</address>
<address>
<street>2 B Street</street>
</address>
<phoneNumber>
<mobileNo>xxx-xxx-xxxx</mobileNo>
</phoneNumber>
</customer>
</configuration>
Following is the Configuration.java
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Configuration {
private Customer customer;
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
#Override
public String toString() {
return "\n Customer[ customer="+customer+"]";
}
}
Customer.java
public class Customer {
private List<ContactInfo> contactInfo;
#XmlElementRef
public List<ContactInfo> getContactInfo() {
return contactInfo;
}
public void setContactInfo(List<ContactInfo> contactInfo) {
this.contactInfo = contactInfo;
}
}
Address.java
public class Address extends ContactInfo {
private String street;
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
}
PhoneNumber.java
public class PhoneNumber extends ContactInfo{
private String mobileNo;
public String getMobileNo() {
return mobileNo;
}
public void setMobileNo(String mobileNo) {
this.mobileNo = mobileNo;
}
}
Demo.java
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class Demo {
public static void main(String[] args) throws Exception {
Configuration configuration = new Configuration();
Customer customer = new Customer();
List<ContactInfo> contacts = new ArrayList<ContactInfo>();
Address address = new Address();
address.setStreet("1 A Street");
contacts.add(address);
Address address1 = new Address();
address1.setStreet("2 B Street");
contacts.add(address1);
PhoneNumber phone = new PhoneNumber();
phone.setMobileNo("408 431 8829");
contacts.add(phone);
customer.setContactInfo(contacts);
configuration.setCustomer(customer);
JAXBContext jc = JAXBContext.newInstance(Configuration.class);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(configuration, System.out);
}
}
Presently I am getting following Exception
Exception in thread "main" com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
Invalid #XmlElementRef : Type "class Address" or any of its subclasses are not known to this context.
Could anybody help me out on this?
Thanks,
Kwatra
Issue #1 - The Subclasses
A JAXB (JSR-222) implementation can not auto discover subclasses. You can solve the first exception by using an #XmlSeeAlso annotation on the ContactInfo class to reference the subclasses:
#XmlSeeAlso({Address.class, PhoneNumber.class})
public class ContactInfo {
}
Or you can reference them when you create the JAXBContext.
JAXBContext jc = JAXBContext.newInstance(Configuration.class, Address.class, PhoneNumber.class);
Issue #2 - The Mapping
When using #XmlElementRef you need to pair it with #XmlRootElement. If you don't want to go this route you could use #XmlElements instead.
#XmlElements({
#XmlElement(name="address", type=Address.class),
#XmlElement(name="phoneNumber", type=PhoneNumber.class)
})
public List<ContactInfo> getContactInfo() {
return contactInfo;
}
http://blog.bdoughan.com/2010/10/jaxb-and-xsd-choice-xmlelements.html
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.