How to map a XML ignoring namespace using JAXB? - java

I'm receiving a XML from a web service with the following format:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<string xmlns="http://someurl.com">somethingheresomethinghere</string>
And I'm trying to unmarshall this to a POJO that looks like this:
#XmlRootElement(name="string")
public class StringValue {
#XmlValue
private String value;
public StringValue () {
}
}
And my unmarshalling code is this:
public T xmlToObject(Class<T> contextPath, Reader reader) throws Exception {
JAXBContext context = JAXBContext.newInstance(contextPath);
Unmarshaller unmarshaller = context.createUnmarshaller();
unmarshaller.setEventHandler(new XMLValidationEventHandler());
T objectToConvert = (T) unmarshaller.unmarshal(reader);
return objectToConvert;
}
But I'm getting the following error:
unexpected element (uri:"http://someurl.com", local:"string"). Expected
elements are <{}string>
If the namespace in the XML were to be omitted from it, then it will work, but instead of altering the XML I want to know how can I make it so the namespace part doesn't cause me any trouble when marshalling or unmarshalling with JAXB.
How can I achieve this behaviour?

You can map the namespace using the package level #XmlSchema annotation.
#XmlSchema(
namespace = "http://someurl.com",
elementFormDefault = XmlNsForm.QUALIFIED)
package example;
import javax.xml.bind.annotation.*;
For More Information
I have written more about JAXB and namespace qualification on my blog:
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html

Related

Namespace with no prefix in JAXB

I am trying to create a Sitemap index file with JAXB. Following the requirements for creating the sitemap, I have to add the namespace attribute in the root element:
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
I would like to have a simple way to sort this out. Since this seems to be a standard procedure I would like to not do a complex workaround or add more dependencies to my project in order to solve this issue
The current output is the following:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:sitemapindex xmlns:ns2="http://www.sitemaps.org/schemas/sitemap/0.9">
<sitemap>
<loc>https://www.example.com/sitemap-1.xml</loc>
<lastmod>2017-05-01T15:41:17.561+01:00</lastmod>
</sitemap>
</ns2:sitemapindex>
My SitemapIndex model is the following:
#XmlRootElement(name = "sitemapindex", namespace="http://www.sitemaps.org/schemas/sitemap/0.9")
#XmlAccessorType(XmlAccessType.FIELD)
public class SitemapIndex {
#XmlElement(name = "sitemap")
private List<Sitemap> sitemaps;
public void setSitemaps(List<Sitemap> sitemaps) {
this.sitemaps = sitemaps;
}
public List<Sitemap> getSitemaps() {
return sitemaps;
}
}
I have also tried to add the namespace field manually and it works for generating the file but I an exception is thrown when I try to read the file.
#XmlAttribute(name="xmlns")
private final String namespace ="http://www.sitemaps.org/schemas/sitemap/0.9";
You can use #XmlSchmea [1] package level annotation in order to set prefix. In your case, we just set empty prefix.
Simply create package-info.java file in your package e.g. com.stackoverflow.jaxb, with similar content.
package-info.java
#XmlSchema(namespace = "http://www.sitemaps.org/schemas/sitemap/0.9",
xmlns = {#XmlNs(prefix = "",
namespaceURI = "http://www.sitemaps.org/schemas/sitemap/0.9")},
elementFormDefault = XmlNsForm.QUALIFIED)
package com.stackoverflow.jaxb;
import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
Note, that you can remove namespace attribute from #XmlRootElement annotation.
[1] https://jaxb.java.net/nonav/2.2.4/docs/api/javax/xml/bind/annotation/XmlSchema.html

Unmarshalling fails with no errors when setting namespace to #XmlRootElement

I have created a JAXB object and I am trying to unmarshal an xml string into it.
The problem that I am facing is that when I put the namespace property in the #XmlRootElement and in the xml document that I am sending, the JAXB object is getting created but it is empty. If I remove the namespace it works. So here is what I mean
My JAXB Object:
#XmlRootElement(name = "incident", namespace = "http://www.ba.com/schema/BAserviceDeskAPI/incident")
#XmlAccessorType(XmlAccessType.FIELD)
public class Incident {
#XmlElement
private String eventTitle;
public Incident() {
}
public String getEventTitle() {
return eventTitle;
}
public void setEventTitle(String eventTitle) {
this.eventTitle = eventTitle;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Incident [");
builder.append("eventTitle=");
builder.append(eventTitle);
builder.append("]");
return builder.toString();
}
}
My Main:
public static void main(String[] args) throws JAXBException {
String s = "<incident xmlns=\"http://www.ba.com/schema/BAserviceDeskAPI/incident\">"
+ "<eventTitle>Test Title from BAwrapper</eventTitle>"
+ "</incident>";
JAXBContext jaxbContext = JAXBContext.newInstance(Incident.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Incident incident = (Incident) jaxbUnmarshaller.unmarshal(new ByteArrayInputStream(s.getBytes(StandardCharsets.UTF_8)));
System.out.println(incident.toString());
}
}
Output:
Incident [eventTitle=null]
If I remove the , namespace = "http://www.ba.com/schema/BAserviceDeskAPI/incident" from the #XmlRootElement and the xmlns=\"http://www.ba.com/schema/BAserviceDeskAPI/incident\" from the xml sent I get the output below
Incident [eventTitle=Test Title from BAwrapper]
Any ideas why this happens?
Thanks
The namespace specified on #XmlRootElement only applies to that element. If you want it to apply to all the elements you have mapped to, you can do it at the package level using the #XmlSchema annotation.
package-info.java
#XmlSchema(
namespace = "http://www.ba.com/schema/BAserviceDeskAPI/incident",
elementFormDefault = XmlNsForm.QUALIFIED)
package example;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
For More Information
I have written more about JAXB and namespace qualification on my blog:
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html
Additional Info
Unmarshalling fails with no errors when setting namespace to
#XmlRootElement
For JAXB we (the JSR-222 expert group) decided that an unmarshal shouldn't fail by default if there is unmapped content. Why? Because alot of XML documents contain extra content and things would be failing all the time. If you do want to see these errors then you can specify a ValidationEventHandler on the Unmarshaller.

MOXy Dynamix JAXB context unmarshalls to wrong property names

I am currently trying to unmarshall the following XML file using the DynamicJaxbContext from MOXy:
<?xml version="1.0" encoding="UTF-8"?>
<request xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://somehost.com/schema_schedule_request.xsd">
<header>
<originator>MySatelliteCompany</originator>
<recipient>Recipient</ recipient>
<schedule_valid_from>2008-12-17T00:00:00Z</schedule_valid_from>
<schedule_valid_to>2008-12-18T00:00:07Z</schedule_valid_to>
<request_reference>HGT4285T3</request_reference>
<generation_time>2008-12-16T08:24:00Z</generation_time>
</header>
<body>
<schedule_request>
<start_time>2008-12-17T09:30:47Z</start_time>
<end_time>2008-12-17T09:33:47Z</end_time>
<satellite_name>MySat</satellite_name>
</schedule_request>
</body>
</request>
It works but the dynamically created Java classes' properties do not correspond to the fields given in the XML. For example: <satellite_name> is unmarshalled to "satelliteName". This makes writing a custom binding file for my backend API quite difficult, because I would have to first either unmarshall all XML files I will get as Input and manually write down the corresponding property names or write another helper app which does this for me.
Is there any way to change this MOXy behavior so it unmarshalls the field names correctly as they are in the XML?
ADDITION:
So I found why this is in the MOXy Documentation:
XML names found in the metadata (complex type names, element names,
attribute names) will be translated to Java identifiers according to
the algorithms described in "Appendix D: Binding XML Names to Java
Identifiers" of the Java Architecture for XML Binding (JAXB) 2.2
Specification (http://jcp.org/en/jsr/detail?id=222). - See more at:
http://www.eclipse.org/eclipselink/documentation/2.4/moxy/dynamic_jaxb001.htm#BABCDJDF
but my principle question still stands: is there any way to shut this off or modify this behavior?
Your ADDITION is correct, MOXy isn't unmarshalling wrong property names, it just unmarshals to property names that correspond to what the mapped property/field names would be in the generated classes.
What the Solution Should Be
binding.xml
The default XML Schema to Java naming rules in JAXB is to remove the _. You can supply a binding file to have this behaviour be different.
<jaxb:bindings
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
version="2.1">
<jaxb:globalBindings underscoreBinding="asCharInWord"/>
</jaxb:bindings>
Demo
Using MOXy's Dynamic JAXB, below is an example of how you can leverage the bindings file.
import java.io.File;
import java.util.*;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
import org.eclipse.persistence.dynamic.DynamicEntity;
import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContextFactory;
public class Demo {
public static void main(String[] args) throws Exception {
StreamSource schemaSource = new StreamSource("src/forum20146935/schema.xsd");
Map<String, Object> properties = new HashMap<String, Object>(1);
properties.put(DynamicJAXBContextFactory.EXTERNAL_BINDINGS_KEY, new StreamSource("src/forum20146935/binding.xml"));
JAXBContext jc = DynamicJAXBContextFactory.createContextFromXSD(schemaSource, null, Demo.class.getClassLoader(), properties);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum20146935/input.xml");
DynamicEntity root = (DynamicEntity) unmarshaller.unmarshal(xml);
System.out.println(root.get("foo_bar"));
}
}
Why Didn't it Work?
As I mentioned earlier MOXy will base the dynamic property name based on the corresponding mapped field/property generated by XJC. This happens to look something like where the property name has the _ but the corresponding mapped field does not.
#XmlElement(name = "foo_bar", required = true)
protected String fooBar;
public String getFoo_Bar() {
return fooBar;
}
public void setFoo_Bar(String value) {
this.fooBar = value;
}
What you Could Do Instead
Use the transformed key name.
You could use the getValueByXPath functionality on the MOXy JAXBContext to interact with the objects in a more XML like way.
import java.io.File;
import java.util.*;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
import org.eclipse.persistence.dynamic.DynamicEntity;
import org.eclipse.persistence.jaxb.JAXBHelper;
import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContextFactory;
public class Demo {
public static void main(String[] args) throws Exception {
StreamSource schemaSource = new StreamSource("src/forum20146935/schema.xsd");
Map<String, Object> properties = new HashMap<String, Object>(1);
properties.put(DynamicJAXBContextFactory.EXTERNAL_BINDINGS_KEY, new StreamSource("src/forum20146935/binding.xml"));
JAXBContext jc = DynamicJAXBContextFactory.createContextFromXSD(schemaSource, null, Demo.class.getClassLoader(), properties);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum20146935/input.xml");
DynamicEntity root = (DynamicEntity) unmarshaller.unmarshal(xml);
String fooBar = JAXBHelper.getJAXBContext(jc).getValueByXPath(root, "foo_bar/text()", null, String.class);
System.out.println(fooBar);
}
}

How to make class to be parsable by JAXB?

Hi I need to create following XML using JAXB but since it has many parent-child relationships , I don't want to make as many classes to create that XML.
Anyone can give idea about how I can make this XML with the help of single class...
<Info>
<details>
<arrange>
<name>joseph</name>
<ID>12</ID>
<Date>2012-03-25T11:23:42+10:00</Date>
<LatestDate>
<Start>2012-06-25T09:24:59+10:00</Start>
<End>2013-06-25T09:24:59+10:00</End>
</LatestDate>
<Additional>
<name>IVR</name>
</Additional>
</arrange>
</details>
</Info>
Write an XSD and use JAXB's xjc code generator to create the classes.
#XmlElementWrapper will do the job, you have tor write a single class and define every element with its wrapping element as you can read here: JAXB unmarshalling multiple XML elements into single class
You have to add the needed JAXB-Annotations to your class.
Then you will be able to parse a XML-File and get the Java-Objects.
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
Since you are looking to map to the XML with a single class you can use MOXy's #XmlPath extension (see: http://blog.bdoughan.com/2010/07/xpath-based-mapping.html).
Info
import java.util.Calendar;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement(name="Info")
#XmlAccessorType(XmlAccessType.FIELD)
public class Info {
#XmlPath("details/arrange/name/text()")
private String name;
#XmlPath("details/arrange/ID/text()")
private int id;
#XmlPath("details/arrange/Date/text()")
private Calendar date;
#XmlPath("details/arrange/LatestDate/Start/text()")
private Calendar start;
#XmlPath("details/arrange/LatestDate/End/text()")
private Calendar end;
#XmlPath("details/arrange/Additional/name/text()")
private String additionalName;
}
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
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Info.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum16956564/input.xml");
Info info = (Info) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(info, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8"?>
<Info>
<details>
<arrange>
<name>joseph</name>
<ID>12</ID>
<Date>2012-03-25T11:23:42+10:00</Date>
<LatestDate>
<Start>2012-06-25T09:24:59+10:00</Start>
<End>2013-06-25T09:24:59+10:00</End>
</LatestDate>
<Additional>
<name>IVR</name>
</Additional>
</arrange>
</details>
</Info>

JAXB: How to avoid repeated namespace definition for xmlns:xsi

I have a JAXB setup where I use a #XmlJavaTypeAdapter to replace objects of type Person with objects of type PersonRef that only contains the person's UUID. This works perfectly fine. However, the generated XML redeclares the same namespace (xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance") every time it's used. While this is generally okay, it just doesn't feel right.
How can I configure JAXB to declare xmlns:xsi at the very beginning of the document? Can I manually add namespace declarations to the root element?
Here's an example of what I want to achive:
Current:
<person uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a">
<relation type="CHILD">
<to xsi:type="personRef" uuid="56a930c0-5499-467f-8263-c2a9f9ecc5a0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
</relation>
<relation type="CHILD">
<to xsi:type="personRef" uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
</relation>
<!-- SNIP: some more relations -->
</person>
Wanted:
<person uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<relation type="CHILD">
<to xsi:type="personRef" uuid="56a930c0-5499-467f-8263-c2a9f9ecc5a0"/>
</relation>
<relation type="CHILD">
<to xsi:type="personRef" uuid="6ec0cf24-e880-431b-ada0-a5835e2a565a"/>
</relation>
<!-- SNIP: some more relations -->
</person>
Not that pretty but you could add an empty schemaLocation to the root element:
marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, "");
It looks like a JAXB customization Namespace mapper issue
When you marshall an XML document using JAXB 1.0, a Marshaller object, a JAXB object that controls the process of marshalling, provides namespace declarations in the resulting XML document. Sometimes the Marshaller produces a lot of namespace declarations that look redundant, for example:
<?xml version="1.0"?>
<root>
<ns1:element xmlns:ns1="urn:foo"> ... </ns1:element>
<ns2:element xmlns:ns2="urn:foo"> ... </ns2:element>
<ns3:element xmlns:ns3="urn:foo"> ... </ns3:element>
</root>
JAXB 2.0 changes this behavior. If you use JAXB 2.0 (or later) to marshal an XML document, the Marshaller declares all statically known namespace Uniform Resource Identifiers (URIs), that is, those URIs that are used as element or attribute names in JAXB annotations.
JAXB may also declare additional namespaces in the middle of an XML document, for example when a qualified name (QName) that is used as an attribute or element value requires a new namespace URI, or when a Document Object Model (DOM) node in a content tree requires a new namespace URI. This behavior might produce an XML document that has a lot of namespace declarations with automatically-generated namespace prefixes.
The problem is that automatically-generated namespace prefixes such as ns1, ns2, and ns3, are not user friendly -- they typically do not help people understand the marshalled XML.
Fortunately, JAXB 2.0 (or later) provides a service provider interface (SPI) named com.sun.xml.bind.marshaller.NamespacePrefixMapper that you can use to specify more helpful namespace prefixes for marshalling.
When the JAXBSample program marshalls the XML document the first time, it does it without using a NamespacePrefixMapper class. As a result, the Marshaller automatically generates a namespace prefix, in this case, ns2.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:JustAnElement xmlns:ns2="a">
<foo>true</foo>
</ns2:JustAnElement>
Example of a configuration avoiding the namespace repetition:
The second marshalling done by the JAXBSample program uses a NamespacePrefixMapper class as follows:
NamespacePrefixMapper m = new PreferredMapper();
marshal(jc, e, m);
public static class PreferredMapper extends NamespacePrefixMapper {
#Override
public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
return "mappedNamespace" + namespaceUri;
}
}
The getPreferredPrefix() method in the PreferredMapper class returns the preferred prefix, in this case, mappedNamespacea to be declared at the root element of the marshalled XML.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<mappedNamespacea:JustAnElement xmlns:mappedNamespacea="a">
<foo>true</foo>
</mappedNamespacea:JustAnElement>
You can do it with the code:
marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new NamespacePrefixMapper() {
#Override
public String[] getPreDeclaredNamespaceUris() {
return new String[] {
XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI
};
}
#Override
public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
if (namespaceUri.equals(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI))
return "xsi";
if (namespaceUri.equals(XMLConstants.W3C_XML_SCHEMA_NS_URI))
return "xs";
if (namespaceUri.equals(WellKnownNamespace.XML_MIME_URI))
return "xmime";
return suggestion;
}
});
if you're using Maven then just add this to your pom:
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.2.2</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
no need for PreferredMapper if you configure your annotations as defined in the example above. Although I have a package-info.jave file confugures as follows:
#javax.xml.bind.annotation.XmlSchema(
namespace = "mylovelynamespace1",
xmlns = {
#javax.xml.bind.annotation.XmlNs(prefix = "myns1", namespaceURI = "mylovelynamespace1"),
#javax.xml.bind.annotation.XmlNs(prefix = "myns2", namespaceURI = "mylovelynamespace2")
},
elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package com.mylovelycompanyname.package;
This is the best answer I find it in the web.
The xsi:type declarations are most likely being created because the declared type of the JAXBElement does not match the type of the value.
If the ObjectFactory has a create method for the correct JAXBElement you should use that since it should correctly populate both the QName and the type info; otherwise I would try setting the declared type (second constructor arg) of the JAXBElement to String.class (assuming this is the type of commentTest) instead of CommentType.Comment.
Source:
http://www.java.net/forum/topic/glassfish/metro-and-jaxb/how-do-i-remove-namespace-declarations-child-elements
Owner:
cbrettin
You can let the namespaces be written only once. You will need a proxy class of the XMLStreamWriter and a package-info.java. Then you will do in your code:
StringWriter stringWriter = new StringWriter();
XMLStreamWriter writer = new Wrapper((XMLStreamWriter) XMLOutputFactory
.newInstance().createXMLStreamWriter(stringWriter));
JAXBContext jaxbContext = JAXBContext.newInstance(Collection.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
jaxbMarshaller.marshal(books, writer);
System.out.println(stringWriter.toString());
Proxy class (the important method is "writeNamespace"):
class WrapperXMLStreamWriter implements XMLStreamWriter {
private final XMLStreamWriter writer;
public WrapperXMLStreamWriter(XMLStreamWriter writer) {
this.writer = writer;
}
//keeps track of what namespaces were used so that not to
//write them more than once
private List<String> namespaces = new ArrayList<String>();
public void init(){
namespaces.clear();
}
public void writeStartElement(String localName) throws XMLStreamException {
init();
writer.writeStartElement(localName);
}
public void writeStartElement(String namespaceURI, String localName) throws XMLStreamException {
init();
writer.writeStartElement(namespaceURI, localName);
}
public void writeStartElement(String prefix, String localName, String namespaceURI) throws XMLStreamException {
init();
writer.writeStartElement(prefix, localName, namespaceURI);
}
public void writeNamespace(String prefix, String namespaceURI) throws XMLStreamException {
if(namespaces.contains(namespaceURI)){
return;
}
namespaces.add(namespaceURI);
writer.writeNamespace(prefix, namespaceURI);
}
// .. other delegation method, always the same pattern: writer.method() ...
}
package-info.java:
#XmlSchema(elementFormDefault=XmlNsForm.QUALIFIED, attributeFormDefault=XmlNsForm.UNQUALIFIED ,
xmlns = {
#XmlNs(namespaceURI = "http://www.w3.org/2001/XMLSchema-instance", prefix = "xsi")})
package your.package;
import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
It's XML, so you could process the output using DOM or XSLT to get rid of multiple namespace references.
Add your nsPrefix mapping by doing this:
marshaller.setNamespaceMapping("myns","urn:foo");

Categories

Resources