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>
Related
This question already has answers here:
JAXB Compiler and Attribute Order [duplicate]
(3 answers)
Closed 5 years ago.
I have two classes:
#XmlSeeAlso(ListType.class)
public class Type {
private int id;
public int getId() {
return id;
}
#XmlAttribute
public void setId(int id) {
this.id = id;
}
}
#XmlRootElement
public class ListType extends Type {
private String name;
private String namespace;
public String getName() {
return name;
}
#XmlAttribute
public void setName(String name) {
this.name = name;
}
public String getNamespace() {
return namespace;
}
#XmlAttribute
public void setNamespace(String namespace) {
this.namespace = namespace;
}
}
And I make marshalling:
ListType listType = new ListType();
listType.setId(123);
listType.setName("Name");
listType.setNamespace("Namespace");
JAXBContext jaxbContext = JAXBContext.newInstance(UserType.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(listType, System.out);
Finally, I get XML like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<listUserType name="Name" namespace="Namespace" id="123"/>
All is well, just tell me, how can I specify the order of the attributes in my XML?
I want them to go in this order:
<listType id="123" name="Name" namespace="Namespace" />
This is very necessary for solving my task. Thank you
Annotate the parent class with #XmlTransient, it will help you to include the properties from parent class then
add this #XmlType(propOrder={"Id", "Name", "Namespace"}) on top of ListUserType class.
I want to unmarshal this xml document that I am receiving from a REST call:
<ns2:hello xmlns:ns4="http://myspace.org/hello/history/1.0" xmlns:ns3="http://www.hello.com/IAP/im1_1_0" xmlns:ns2="http://www.w3.org/2005/Atom">
<ns3:totalEntries>7</ns3:totalEntries>
<ns2:id>123</ns2:id>
<ns2:title type="text">Users</ns2:title>
<ns2:updated>2017-08-22T07:51:27.270Z</ns2:updated>
<ns2:link href="https://example.com:8080/1/rest/users" rel="self"/>
<ns2:link href="https://example.com:8080/1/rest/users" rel="http://www.example.com/iap/im/user/create"/>
<ns4:complete/>
<ns2:entry>
<ns2:id>urn:uuid:f0fd4040-04da-11e7-8f6a-8e3ecfcb7035</ns2:id>
<ns2:title type="text">Hello</ns2:title>
<ns2:content type="application/vnd.bosch-com.im+xml">
<ns3:user>
<ns3:id>f0fd4040-04da-11e7-8f6a-8e3ecfcb7035</ns3:id>
<ns3:name>name</ns3:name>
<ns3:firstName>Hello</ns3:firstName>
<ns3:lastName>All</ns3:lastName>
</ns3:user>
</ns2:content>
</ns2:entry>
</ns2:hello>
As you can see the XML is nested, and for this I am using JAXB for unmarshalling:
try {
JAXBContext jc = JAXBContext.newInstance(Feed.class);
Unmarshaller um = jc.createUnmarshaller();
Feed feed = (Feed) um.unmarshal(new StringReader(userEntity.getBody()));
System.out.println(feed.getEntry().get(0).getContent().getUser().getFirstName());
}
catch (JAXBException e) {
e.printStackTrace();
}
But, I am not getting any data set into my POJO (it's null):
#XmlRootElement(namespace="http://www.w3.org/2005/Atom", name="hello")
public class Hello {
private String id;
private String totalEntries;
private String title;
private String updated;
List<Entry> entry;
Complete complete;
}
My POJO looks like above. Also I have created Entry and Complete POJO classes. How can I fix this?
The XML has 3 namespaces:
xmlns:ns4="http://myspace.org/hello/history/1.0"
xmlns:ns3="http://www.hello.com/IAP/im1_1_0"
xmlns:ns2="http://www.w3.org/2005/Atom"
You will need to ensure that each element is in the correct namespace #XmlElement(namespace="xmlns:ns3="http://www.hello.com/IAP/im1_1_0") etc, to be able to unmarshall correctly
You may Also benefit from adding #XmlType(propOrder = { "totalEntries", ...
Have you tried just adding getters and setters to the class Hello ?
Your sample works with this simplified version of Hello.
#XmlRootElement(namespace = "http://www.w3.org/2005/Atom", name = "hello")
public class Hello {
private String id;
private String title;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
And the Test class
public class Test {
public static void main(String[] args) {
try {
JAXBContext jc = JAXBContext.newInstance(Feed.class);
Unmarshaller um = jc.createUnmarshaller();
InputStream file = new FileInputStream("path/to/file/feed.xml");
Hello feed = (Hello) um.unmarshal(file);
System.out.println(feed.getId());
System.out.println(feed.getTitle());
} catch (JAXBException | FileNotFoundException e) {
e.printStackTrace();
}
}
}
Prints
123
Users
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'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.
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.