JAXB: need to set namespace dynamically - java

JAXB annotated class:
#XmlRootElement(name = "group")
#XmlType(propOrder = {"name", "description", "types" })
public class GroupElement implements Serializable {
private static final long serialVersionUID = 1L;
private long id;
private String description;
private String name;
private List<TypeElement> types;
#XmlTransient
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#XmlElement(name = "description")
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
#XmlElement(name = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#XmlElement(name = "type")
public List<TypeElement> getTypes() {
return types;
}
public void setTypes(List<TypeElement> types) {
this.types= types;
}
}
java-package.info
#XmlSchema(namespace = "http://www.test.at/r1/v1" ,
elementFormDefault = XmlNsForm.QUALIFIED,
xmlns={#XmlNs(prefix="cdr1", namespaceURI="http://www.test.at/r1/v1")})
JAXB marshalling:
JAXBContext jaxbContext = JAXBContext.newInstance(Group.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(group, sw);
I need to set the namespace dynamically depending on the group. With the package-info approach I get the same namespace in every XML file.
I also tried
JAXBElement<Group> element = new JAXBElement<Group>(new
QName(latestStructure.getNamespace(), "group", "cdr1"),
Group.class, group);
But With this approach only the root element gets the namespace and prefix assigned.
Does anyone have an idea how to do this?

You could remove namespace info from the class and package, but create object factories with qualified names (so as many object factories as many namespaces),
#XmlRegistry
public class ObjectFactory {
...
}
and then use the correct factory to initiate your context instead of the Group class:
JAXBContext.newInstance(ObjectFactory.class);
For me easier would be to again use beans without namespaces, marshall to namespace free xml and transform the xml using xslt to add correct "dynamic" namespace as required.

Related

Jackson Xml: How to add namespace only on root?

If i declare the namespace on the root element, like this:
#JacksonXmlRootElement(namespace = "urn:stackify:jacksonxml", localName = "PersonData")
public class Person {
private String id;
private String name;
private String note;
}
It produces:
<PersonData xmlns="urn:stackify:jacksonxml">
<id xmlns="">12345</id>
<name xmlns="">Graham</name>
<note xmlns="">Hello</note>
</PersonData>
But I want the namespace only on the root element. The xmlns attribute should not appear on child elements.
How can i archive this?
There is a workaround which I found more elegant for me.
You may define constant for your namespace like this:
#JacksonXmlRootElement(localName = "PersonData")
public class Person {
#JacksonXmlProperty(isAttribute = true)
private final String xmlns = "urn:stackify:jacksonxml";
private String id;
private String name;
private String note;
}
You need to specify the same namespace as the root element in each attribute:
#JacksonXmlRootElement(namespace = "urn:stackify:jacksonxml", localName = "PersonData")
public class Person {
#JacksonXmlProperty(namespace = "urn:stackify:jacksonxml")
private String id;
#JacksonXmlProperty(namespace = "urn:stackify:jacksonxml")
private String name;
#JacksonXmlProperty(namespace = "urn:stackify:jacksonxml")
private String note;
}
Its a bit tiresome, but its the only way I found to avoid the unnecessary namespaces.
Also works well with immutables library and json annotations (if you need to serialize/deserialize both in JSON and in XML)
#Value.Immutable
#JsonRootName(value = "PersonData", namespace = "urn:stackify:jacksonxml")
public interface Person extends Serializable {
}

Attribute order using JAXB [duplicate]

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.

Map Java Object to #XmlElement Value

The LocationType Set coming null when I'm running JAXBxmlToJava. Can any body suggest how to map type component of xml to LocationType Object of Java.
I have following arc-response.xml:-
<address_component>
<long_name>BMC Colony</long_name>
<short_name>BMC Colony</short_name>
<type>neighborhood</type>
<type>political</type>
</address_component>
And Following Code, AddressComponent:-
#XmlRootElement(name = "address_component")
public class AddressComponent {
#XmlElement(name = "long_name")
private String longName;
#XmlElement(name = "short_name")
private String shortName;
#XmlElement(name = "type")
private Set<LocationType> locationTypeSet;
//Setter Getter
}
LocationType:-
#XmlRootElement(name="type")
public class LocationType {
private Integer locationTypeId;
#XmlElement(name = "type")
private String type;
private String status;
//Setter Getter
}
JAXBxmlToJava.java:-
public class JAXBxmlToJava {
public static void main(String[] args) {
try {
File file = new File("arc-response.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(AddressComponent.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
AddressComponent geoResponse = (AddressComponent) jaxbUnmarshaller.unmarshal(file);
System.out.println(geoResponse);
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
You'll need #XmlValue to map a text node (e.g. 'neighborhood') to a field in a custom class.
When I tested your code, the Set<LocationType> wasn't null - there were two LocationTypes in it, but their type field was null. I'm not sure if that is what you meant in your question.
When I changed the LocationType class to
#XmlRootElement(name = "type")
public class LocationType {
private Integer locationTypeId;
#XmlValue
private String type;
private String status;
// Setter Getter
}
it worked.

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.

Exception in thread "main" java.lang.NullPointerException when using Jaxb unmarshal/marshal

I'm using JAXB to unmarshal a given input Xml file into Java object
and then marashal it back to Xml String.
My Xml file looks like this:
<bpmn2:definitions xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" id="_Definitions_1">
<bpmn2:process id="_500441" name="process">
</bpmn2:process>
</bpmn2:definitions>
Definitions.class:
#XmlRootElement(namespace = "http://www.omg.org/spec/BPMN/20100524/MODEL")
public class Definitions {
#XmlAttribute
private String id;
#XmlElement(name = "bpmn2:process")
private Process process;
#XmlElement(name = "bpmndi:BPMNDiagram")
private Diagram diagram;
public Definitions() {
}
public Definitions(String id, Process process, Diagram diagram) {
this.id = id;
this.process = process;
this.diagram = diagram;
}
public Process getProcess() {
return process;
}
public Diagram getDiagram() {
return diagram;
}
public String getId() {
return id;
}
}
Process.class:
#XmlAccessorType(XmlAccessType.FIELD)
public class Process {
#XmlAttribute
private String id;
public Process() {
}
public Process(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
Model.class:
public class Model {
#XmlElement
private Process process;
public Model() {
}
public Model(String processId, Process p) {
this.id = processId;
this.process = p;
}
}
main method:
public static void main(String[] args) throws IOException, JSONException, JAXBException {
BpmnToJsonImport bj = new BpmnToJsonImport();
InputStream is = BpmnToJsonImport.class.getResourceAsStream("myXml.txt");
String Str = IOUtils.toString(is);
StringReader sr = new StringReader(Str);
JAXBContext context = JAXBContext.newInstance(Definitions.class, Model.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
Definitions d = (Definitions) unmarshaller.unmarshal(sr);
Model model = new Model(d.getProcess().getId(), d.getProcess());
StringWriter sw = new StringWriter();
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
marshaller.marshal(model, sw);
String str = sw.toString();
System.out.println(str);
}
Exactly when it tries to retrieve the process id using d.getProcess.getId I get the
java.lang.NullPointerException
You are mapping the namespace qualification incorrectly. You must not include the prefix in the element name.
#XmlElement(name = "BPMNDiagram")
private Diagram diagram;
To map the namespace qualification you can use the package level #XmlSchema annotation.
package-info.java
#XmlSchema(
namespace = "http://www.omg.org/spec/BPMN/20100524/MODEL",
elementFormDefault = XmlNsForm.QUALIFIED)
package example;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
For More Information
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html

Categories

Resources