How to set default namespace with JAXB (JDK 7) - java

I've got problem with setting default namespace with JAXB. I have followed tips from here but problem still exist. Here is how looks like my package-info:
#XmlSchema(
namespace = "my/xml/namespace",
xmlns = {
#XmlNs(namespaceURI="my/xml/namespace",prefix=""),
},
elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package my.xml.namespace;
import javax.xml.bind.annotation.XmlSchema;
import javax.xml.bind.annotation.XmlNs;
import javax.xml.XMLConstants;;
But still created xml looks like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:resp xmlns:ns2="my/xml/namespace">
....
</ns2:resp>
I would like to get rid of these prefix but run out of ideas. Anyone maybe know why?
UPDATE
I habe followed advice and implement NamespacePrefixMapper and:
It's crucial to be com.sun.xml.bind.marshaller.NamespacePrefixMapper. I've use another one and get wierd exceptions.
It does not work too - the same issue persist. Here is my mapper:
NamespacePrefixMapper mapper = new NamespacePrefixMapper(){
#Override
public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix)
{
if("amc/xml/pay03_entryresponse".equals(namespaceUri)) {
return "";
}else return suggestion;
}
};
marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", mapper);

You have to use a Namespace mapper.
There are some examples on the web:
http://hwellmann.blogspot.co.at/2011/03/jaxb-marshalling-with-custom-namespace.html
http://java.dzone.com/articles/jaxb-and-namespace-prefixes

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

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);
}
}

JaxB unmarshal custom xml

I'm currently attempting to unmarshal some existing XML into a few classes I have created by hand. Problem is, I always get an error that tells me, JaxB expects a weather element but finds a weather element. (?)
javax.xml.bind.UnmarshalException: unexpected element (uri:"http://www.aws.com/aws", local:"weather"). Expected elements are <{}api>,<{}location>,<{}weather>
I tried with and without "aws:" in the elements' name.
Here's my weather class:
#XmlRootElement(name = "aws:weather")
public class WeatherBugWeather
{
private WeatherBugApi api;
private List<WeatherBugLocation> locations;
private String uri;
#XmlElement(name="aws:api")
public WeatherBugApi getApi()
{
return this.api;
}
#XmlElementWrapper(name = "aws:locations")
#XmlElement(name = "aws:location")
public List<WeatherBugLocation> getLocations()
{
return this.locations;
}
public void setApi(WeatherBugApi api)
{
this.api = api;
}
public void setLocations(List<WeatherBugLocation> locations)
{
this.locations = locations;
}
#XmlAttribute(name="xmlns:aws")
public String getUri()
{
return this.uri;
}
public void setUri(String uri)
{
this.uri = uri;
}
}
And that's the XML I try to parse:
<?xml version="1.0" encoding="utf-8"?>
<aws:weather xmlns:aws="http://www.aws.com/aws">
<aws:api version="2.0" />
<aws:locations>
<aws:location cityname="Jena" statename="" countryname="Germany" zipcode="" citycode="59047" citytype="1" />
</aws:locations>
</aws:weather>
I'm not quite sure what I'm doing wrong. Any hints? I suspect the problem to be the xmlns definition, but I have no idea what to do about it. (You can see that by looking at the uri-property. That was one unsuccessful idea. ^^) And yes, I did try to set the namespace but that rather set's the namespace's uri instead of it's ... name.
I would recommend adding a package-info class in with your domain model with the #XmlSchema annotation to specify the namespace qualification:
package-info
#XmlSchema(
namespace = "http://www.aws.com/aws",
elementFormDefault = XmlNsForm.QUALIFIED)
package com.example.foo;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
Note
Your XmlRootElement and #XmlElement annotation should not contain the namespace prefix. You should have #XmlRootElement(name = "weather") instead of #XmlRootElement(name = "aws:weather")
For More Information
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html
you need namespaces in your code. namespace prefixes are meaningless, you need the actual namespace (i.e. "http://www.aws.com/aws").
#XmlRootElement(name = "weather", namespace="http://www.aws.com/aws")

JAXB 2.x with MOXy (Eclipselink 2.1.2): persisting XHTML as element value

IMPORTANT: updated with solution!
See JAXB 2.x with MOXy (Eclipselink 2.1.2): persisting XHTML as element value
I want to store XHTML as a node value of an XML via JAXB, e.g.
<p>This is a paragraph</p>
should get stored "as is" in an XML:
<root>
<title>Title</title>
<content>...the above XHTML snippet of paragraph...</content>
</root>
As I am using MOXy as JAXB Impl, I wanted to try this (version 2.1.2):
#XmlCDATA
public void setContent(String content) {
this.content = content;
}
However, the content gets marshalled as escaped code and I can't display it afterwards as XHTML snippet.
First of all: Where can I download the latest release of MOXy? It might be a bug in 2.1.2. The maven repo is somehow not available anymore:
http://download.eclipse.org/rt/eclipselink/maven.repo
The link was found at
http://wiki.eclipse.org/EclipseLink/Maven
Thanks
Update and solution regarding MOXy version 2.3.0:
I have tried out version 2.3.0 of EclipseLink now and it works!
Here is my data object that I persist:
import org.eclipse.persistence.oxm.annotations.XmlCDATA;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name=DialogMessage.ROOTNAME, namespace="...")
public class DialogMessage {
#XmlTransient
public static final String ROOTNAME = "dialog";
#XmlElement(name="content-html", namespace="...")
#XmlCDATA
private String contentHTML = null;
public String getContentHTML() {
return contentHTML;
}
public void setContentHTML(String contentHTML) {
this.contentHTML = contentHTML;
}
}
and the MOXy enabling config files:
package-info.java:
#javax.xml.bind.annotation.XmlSchema(
namespace = "...",
xmlns = {#javax.xml.bind.annotation.XmlNs(prefix = "z", namespaceURI ="...")},
elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package com.yourdomain.packagenametodataclass;
and the jaxb.properties:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
When I write an instance of DialogMessage as XML to disk, the XML looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<z:dialog xmlns:z="...">
<z:content-html><![CDATA[<p>sgd <strong>asdf</strong> asdf </p>
<p>asdf</p>]]></z:content-html>
</z:dialog>
Please note that "..." is just a replacement for whatever namespace URI you have.

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