Adding namespaces to root element of xml using jaxb - jaxb

I am creating an xml file whose root elemenet structure shuould be like:
<RootElement xmlns="http://www.mysite.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mysite.com/abc.xsd">
i created package-info.java class but i can get only one namespace by writing this code:
#XmlSchema(
namespace = "http://www.mysite.com",
elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package myproject.myapp;
import javax.xml.bind.annotation.XmlSchema;
Any idea?

Below is some demo code that will produce the XML you are looking for. You can use the Marshaller.JAXB_SCHEMA_LOCATION property to specify the schemaLocation this will cause the http://www.w3.org/2001/XMLSchema-instance namespace to be automatically declared.
Demo
package myproject.myapp;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(RootElement.class);
RootElement rootElement = new RootElement();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, "http://www.mysite.com/abc.xsd");
marshaller.marshal(rootElement, System.out);
}
}
Output
Below is the output from running the demo code.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RootElement xmlns="http://www.mysite.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mysite.com/abc.xsd"/>
package-info
This is the package-info class from your question.
#XmlSchema(
namespace = "http://www.mysite.com",
elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED
)
package myproject.myapp;
import javax.xml.bind.annotation.*;
RootElement
Below is a simplified version of your domain model:
package myproject.myapp;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="RootElement")
public class RootElement {
}

In the older Jaxb you can specify additional namespaces using #XmlSeeAlso and in the newer Jaxb you can use #XmlNs in package-info.java see this answer: https://stackoverflow.com/a/63559294/447503

Related

JAX WS, JAXB and Null elements with attributes

I am trying to consume a web service using JAXWS and wsimport. The WSIMPORT tool generated all the required classes and I can invoke the service without any issues.
However, I noticed in cases where response contains a nil element with valid attribute values, JAXWS fails to unmarshall it and throws a NullPointerException. I used SOAP UI to help debug and here's what I found. The response returns the following XML (Excerpt):
<externalIdentifiers>
<identifierType code="2" name="Passport" xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
<identifierValue/>
<issuingCountry xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
</externalIdentifiers>
In my Java code, when trying to read the "name" property of identifier type as above, it throws a NPE:
if(id.getIdentifierType() == null)
{
System.out.println("NULL");
}
System.out.println("Identifier Type: " + id.getIdentifierType().getName());
Output:
NULL
Exception in thread "main" java.lang.NullPointerException
To me that does looks a reasonable response as in the response, identifierType is set as xsi:nil="true". That is also perfectly valid XML as per W3C. Question is, how do I read the attribute values such as code and name in such a case?
Below is how you can support this use case:
Java Model
ExternalIdentifiers
You can change the identifierType property to be of type JAXBElement<IdentifierType> instead of IdentifierType. To do this you will need to annotate the property with #XmlElementRef.
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class ExternalIdentifiers {
#XmlElementRef(name="identifierType")
private JAXBElement<IdentifierType> identifierType;
public JAXBElement<IdentifierType> getIdentifierType() {
return identifierType;
}
}
ObjectFactory
You will need a corresponding #XmlElementDecl annotation on a create method in a class annotated with #XmlRegistry.
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
import javax.xml.namespace.QName;
#XmlRegistry
public class ObjectFactory {
#XmlElementDecl(name="identifierType")
public JAXBElement<IdentifierType> createIdentifierType(IdentifierType identifierType) {
return new JAXBElement(new QName("identifierType"), IdentifierType.class, identifierType);
}
}
Demo Code
input.xml
<?xml version="1.0" encoding="UTF-8"?>
<externalIdentifiers>
<identifierType code="2" name="Passport" xsi:nil="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />
</externalIdentifiers>
Demo
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(ExternalIdentifiers.class, ObjectFactory.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum18834036/input.xml");
ExternalIdentifiers externalIdentifiers = (ExternalIdentifiers) unmarshaller.unmarshal(xml);
System.out.println(externalIdentifiers.getIdentifierType().getValue().getName());
}
}
Output
Passport
Note
Currently there is a bug in EclipseLink JAXB (MOXy) regarding this use case:
http://bugs.eclipse.org/404944

unmarshalling into single class using eclipselink

i have created a Company class that does produce xml like below using marshalling :
<?xml version="1.0" encoding="UTF-8"?>
<ns2:company xmlns:ns2="http://www.example.com/">
<ns2:employee>
<job>sogi</job>
<name>togi</name>
<age>22</age>
</ns2:employee>
</ns2:company>
Note:I used #XmlPath("employee/job/text()") tag in Company class to get the required path.
but when unmarshalling i use the same Company class,i do not get the correct object values.Instead i get null values.
You need to include namespace information in the #XmlPath annotation.
package-info
Since your XML document has namespace qualification, you will need to leverage the package level #XmlSchema annotation to specify the namespace information.
#XmlSchema(
namespace="http://www.example.com/",
xmlns={
#XmlNs(namespaceURI = "http://www.example.com/", prefix = "foo")
}
)
package forum14848450;
import javax.xml.bind.annotation.*;
Company
In the #XmlPath mapping for the fragments of the XmlPath that are namespace qualified you need to leverage the prefixes you defined on the #XmlSchema annotation.
package forum14848450;
import javax.xml.bind.annotation.XmlRootElement;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement
public class Company {
#XmlPath("foo:employee/job/text()")
private String employeeJob;
#XmlPath("foo:employee/name/text()")
private String employeeName;
#XmlPath("foo:employee/age/text()")
private int employeeAge;
}
jaxb.properties
To specify MOXy as your JAXB provider you need to include a file called jaxb.properties in the same package as your domain model with the following entry (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html).
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
The demo code below will unmarshal the document from your question, and then marshal it back to XML.
package forum14848450;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Company.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum14848450/input.xml");
Company company = (Company) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(company, System.out);
}
}
Output
Below is the output from running the demo code. Note how the output document uses the prefixes defined in the #XmlPath annotation.
<?xml version="1.0" encoding="UTF-8"?>
<foo:company xmlns:foo="http://www.example.com/">
<foo:employee>
<job>sogi</job>
<name>togi</name>
<age>22</age>
</foo:employee>
</foo:company>
For More Information
http://blog.bdoughan.com/2010/09/xpath-based-mapping-geocode-example.html

Jaxb order of the xmlns:xsi and xsi:noNamespaceSchemaLocation

I am using JAXB to create an xml.
Used
marshaller.setProperty(
Marshaller.JAXB_NO_NAMESPACE_SCHEMA_LOCATION,
"bla-bla.xsd");
the xml being generated is
<Interface xsi:noNamespaceSchemaLocation="bla-bla.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
however the application that is parsing this xml for some reason is not parse it as they need it in this format
<Interface xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="bla-bla.xsd">
changing the target application is not an option :(
The following approach leveraging JAXB and StAX appears to give you the desired output, but since the order of attributes is not significant it is not guaranteed to always work.
import javax.xml.bind.*;
import javax.xml.stream.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Interface.class);
XMLOutputFactory xof = XMLOutputFactory.newFactory();
XMLStreamWriter xsw = xof.createXMLStreamWriter(System.out);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_NO_NAMESPACE_SCHEMA_LOCATION, "bla-bla.xsd");
marshaller.marshal(new Interface(), xsw);
}
}
Output
<?xml version="1.0"?><Interface xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="bla-bla.xsd"></Interface>

Add/Override behavior on Jaxb generated classes by extending them

I have a web server responding with xml data and a client consuming it.
Both share the same domain code. One of the domain objects looks like this:
#XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
#XmlRootElement(name = "image")
public class Image {
private String filename;
private ImageTypeEnum type;
#XmlElement(name = "imageUri")
public String getAbsoluteUri() {
// some complex computation
return uri;
}
}
When I try to unmarshal the response from the server into this object, since there's no setter for absoluteUri, I don't have the imageUri in the class. So I extend it like this:
public class FEImage extends Image{
private String imageUri;
public String getAbsoluteUri() {
return imageUri;
}
public void setAbsoluteUri(String imageUri) {
this.imageUri = imageUri;
}
}
My ObjectFactory
#XmlRegistry
public class ObjectFactory {
public Image createImage(){
return new FEImage();
}
}
My code to unmarshal is here:
JAXBContext context = JAXBContext.newInstance(ObjectFactory.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
unmarshaller.setProperty("com.sun.xml.bind.ObjectFactory",new ObjectFactory());
((JAXBElement)unmarshaller.unmarshal((InputStream) response.getEntity())).getValue();
However, the setAbsoluteUri doesn't seem to be getting called in FEImage while unmarshalling. When I add a dummy setAbsoluteUri in Image.java, everything works as expected.
Can someone tell me how can I cleanly extend from Image.java?
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.
A JAXB implementation is not required to use the ObjectFactory class when instantiating an object. You can configure instantiation to be done via a factory class using the #XmlType annotation:
#XmlType(factoryClass=ObjectFactory.class, factoryMethod="createImage")
public class Image {
private String filename;
private ImageTypeEnum type;
#XmlElement(name = "imageUri")
public String getAbsoluteUri() {
// some complex computation
return uri;
}
}
http://blog.bdoughan.com/2011/06/jaxb-and-factory-methods.html
If you do the above, then your JAXB implementation will still use the Image class to derive the metadata so it will not solve your problem. An alternate approach would be to use an XmlAdapter for this use case:
http://blog.bdoughan.com/2010/12/jaxb-and-immutable-objects.html
Better still, when a property on your domain object does not have a setter, you can tell your
JAXB implementation (EclipseLink MOXy, Metro, Apache JaxMe, etc) to use field (instance variable) access instead using #XmlAccessorType(XmlAccessType.FIELD):
#XmlAccessorType(XmlAccessType.FIELD)
public class Image {
}
http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html
UPDATE #1
If you are not able to modify the domain objects, then you may be interested in MOXy's externalized metadata. This extension provides a means via XML to provide JAXB metadata for classes where you cannot modify the source.
For More Information
http://blog.bdoughan.com/2010/12/extending-jaxb-representing-annotations.html
http://wiki.eclipse.org/EclipseLink/UserGuide/MOXy/Runtime/XML_Bindings
UPDATE #2 - Based on results of chat
Image
Below is the implementation of the Image class that I will use for this example. For the complex computation of getAbsoluteUri() I simply add the prefix "CDN" to the filename:
package forum7552310;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
#XmlRootElement(name = "image")
public class Image {
private String filename;
private ImageTypeEnum type;
#XmlElement(name = "imageUri")
public String getAbsoluteUri() {
return "CDN" + filename;
}
}
binding.xml
Below is the MOXy binding document I put together. In this file I do a few things:
Set XmlAccessorType to FIELD
Mark the absoluteURI property to be XmlTransient since we will be mapping the filename field instead.
Specify that an XmlAdapter will be used with the filename field. This is to apply the logic that is done in the getAbsoluteUri() method.
<?xml version="1.0"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="forum7552310">
<java-types>
<java-type name="Image" xml-accessor-type="FIELD">
<java-attributes>
<xml-element java-attribute="filename" name="imageUri">
<xml-java-type-adapter value="forum7552310.FileNameAdapter"/>
</xml-element>
<xml-transient java-attribute="absoluteUri"/>
</java-attributes>
</java-type>
</java-types>
</xml-bindings>
FileNameAdapter
Below is the implementation of the XmlAdapter that applies the same name algorithm as the getAbsoluteUri() method:
package forum7552310;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class FileNameAdapter extends XmlAdapter<String, String> {
#Override
public String marshal(String string) throws Exception {
return "CDN" + string;
}
#Override
public String unmarshal(String adaptedString) throws Exception {
return adaptedString.substring(3);
}
}
Demo
Below is the demo code demonstrating how to apply the binding file when creating the JAXBContext:
package forum7552310;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import org.eclipse.persistence.jaxb.JAXBContextFactory;
public class Demo {
public static void main(String[] args) throws Exception {
Map<String, Object> properties = new HashMap<String, Object>(1);
properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "forum7552310/binding.xml");
JAXBContext jc = JAXBContext.newInstance(new Class[] {Image.class}, properties);
File xml = new File("src/forum7552310/input.xml");
Unmarshaller unmarshaller = jc.createUnmarshaller();
Image image = (Image) unmarshaller.unmarshal(xml);
System.out.println(image.getAbsoluteUri());
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(image, System.out);
}
}
jaxb.properties
You need to include a file named jaxb.properties with the following contents in the same package as your Image class:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
input.xml
Here is the XML input I used:
<?xml version="1.0" encoding="UTF-8"?>
<image>
<imageUri>CDNURI</imageUri>
</image>
Output
And here is the output from running the demo code:
CDNURI
<?xml version="1.0" encoding="UTF-8"?>
<image>
<imageUri>CDNURI</imageUri>
</image>

Jaxb marshaller always writes xsi:nil (even when #XmlElement(required=false, nillable=true))

I have a java property annotated with #XmlElement(required=false, nillable=true). When the object is marshalled to xml, it is always outputted with the xsi:nil="true" attribute.
Is there a jaxbcontext/marshaller option to direct the marshaller not to write the element, rather than write it with xsi:nil?
I've looked for answers to this and also had a look at the code, afaics, it will always write xsi:nil if nillable = true. Am I missing something?
If the property is annotated with #XmlElement(required=false, nillable=true) and the value is null it will be written out with xsi:nil="true".
If you annotate it with just #XmlElement you will get the behaviour you are looking for.
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
Example
Given the following class:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
#XmlElement(nillable=true, required=true)
private String elementNillableRequired;
#XmlElement(nillable=true)
private String elementNillbable;
#XmlElement(required=true)
private String elementRequired;
#XmlElement
private String element;
}
And this demo code:
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Root root = new Root();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
The result will be:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<elementNillableRequired xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
<elementNillbable xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
</root>

Resources