JAXB 2.x : Marshalling puts element value twice into the XML - java

For some reason, all values of an element gets written twice.
My test case is very simple:
package test;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlAccessorType(XmlAccessType.NONE)
#XmlRootElement(name="root")
public class TestBean {
private String name = null;
#XmlElement(name="lastname")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Then I marshall the document to the filesystem into an XML:
TestBean object = new TestBean();
object.setName("abc ");
Class<?> clazz = object.getClass();
JAXBContext context = JAXBContext.newInstance(clazz);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE );
m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
m.marshal(object, new File("test.xml"));
And the resulting XML is:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<lastname>abc abc </lastname>
</root>
For simplicity I removed the package-info.java file with the namespace definitions.
The implementation I am using is org.eclipse.persistence.moxy 2.1.2:
the jaxb.properties file in the package folder contains this line:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Thanks for any hints.

This is a known MOXy issue that has been fixed in the EclipseLink 2.3.0 stream. An EclipseLink 2.3.0 download can be obtained here:
http://www.eclipse.org/eclipselink/downloads/nightly.php
The workaround for EclipseLink 2.1.2 is to use another access type, or to annotate the corresponding field with #XmlTransient:
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
#XmlAccessorType(XmlAccessType.NONE)
#XmlRootElement(name="root")
public class TestBean {
#XmlTransient
private String name = null;
#XmlElement(name="lastname")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

I tried your test and it gives the correct output for me:
<root>
<lastname>abc </lastname>
</root>
It could be the JAXB2 implementation (moxy in your case vs native JDK1.6 based JAXB2 for my test).

Related

Unmarshalling a simple XML

I am having hard time unmarshalling the following XML using JAXB. This is the xml which is having only one field with an attribute. I have referred many tutorials where they only do an example of reading a string in fields which s not helpful in my case.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<CircuitImpactConfigs id="1">
<Objects>
<Object type="1" impactAnalysisDataBuilderClassName="tttttttt"/>
<Object type="2" impactAnalysisDataBuilderClassName="yyyyyyyyy"/>
<Object type="3" impactAnalysisDataBuilderClassName="eeeeeee" />
<Object type="4" impactAnalysisDataBuilderClassName="iiiiiiiii"/>
<Object type="5" impactAnalysisDataBuilderClassName="rrrrrrrrrr"/>
<Object type="6" impactAnalysisDataBuilderClassName="zzzzzz"/>
<Object type="7" impactAnalysisDataBuilderClassName="qqqqqqqqqqq"/>
</Objects>
<ForceSwitchMode name="FORCE_SWITCHED" />
</CircuitImpactConfigs>
Based on what i learnt from tutorial
My Classes are CircuitImpactConfigs.java
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement(name = "CircuitImpactConfigs")
#XmlAccessorType(XmlAccessType.FIELD)
public class CircuitImpactConfigs {
private ForceSwitchMode ForceSwitchMode;
List<Obj> Object;
#XmlElement
public List<Obj> getObject() {
return Object;
}
public void setObject(List<Obj> object) {
Object = object;
}
#XmlElement
public ForceSwitchMode getForceSwitchMode() {
return ForceSwitchMode;
}
public void setForceSwitchMode(ForceSwitchMode forceSwitchMode) {
ForceSwitchMode = forceSwitchMode;
}
}
and
import javax.xml.bind.annotation.XmlAttribute;
public class ForceSwitchMode {
private String name;
#XmlAttribute
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
and
import javax.xml.bind.annotation.XmlAttribute;
public class Obj {
String type;
String impactAnalysisDataBuilderClassName;
#XmlAttribute
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
#XmlAttribute
public String getImpactAnalysisDataBuilderClassName() {
return impactAnalysisDataBuilderClassName;
}
public void setImpactAnalysisDataBuilderClassName(String impactAnalysisDataBuilderClassName) {
this.impactAnalysisDataBuilderClassName = impactAnalysisDataBuilderClassName;
}
}
I am getting null list when doing the unmarshalling. This is the class where i create the JAXBcontext object and create unmarshalling object.
import java.io.File;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
public class CircuitImpactConfUnmarshaller {
public static void main(String[] args) throws JAXBException {
File file = new File("CircuitImpact.xml");
System.out.println(file.exists());
JAXBContext jaxbContext = JAXBContext.newInstance(CircuitImpactConfigs.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
CircuitImpactConfigs que = (CircuitImpactConfigs) jaxbUnmarshaller.unmarshal(file);
System.out.println(que.getForceSwitchMode().getName());
List<Obj> list = que.getObject();
System.out.println(list);
}
}
the last print statement is giving null. I understand i am doing something wrong in the class Obj
JAXB uses implicit naming conventions and explicit annotations to define a mapping between a XML and a Java structure.
Either element and attribute names in the XML match field names in Java (match by naming convention) or you need to use annotations to establish the mapping.
The Java list CircuitImpactConfigs.Object is not getting filled because the mapping failed, since the corresponding element in the XML is named Objects.
You can now either rename CircuitImpactConfigs.Object to CircuitImpactConfigs.Objects or use the name parameter of a JAXB annotation to define the corresponding name:
#XmlElement(name="Objects")
public List<Obj> getObject() {
EDIT: As you indicate in your comments there are still other mapping issues with your code. I would suggest that you adapt another approach:
Create a CircuitImpactConfigs object with all subobjects filled.
Marhsall that object to a XML file.
Check that the XML is in the expected format. If not, tweak the mapping.
Following this approach you can be sure that a XML file in the desired format can be unmarshalled to your Java structure. The code to marshall is
CircuitImpactConfigs que = ...
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(que, System.out);

Marshalling/unmarshalling fields to tag with attributes using JAXB

Let's say I have class Example:
class Example{
String myField;
}
I want to unmarshal it in this way:
<Example>
<myField value="someValue" />
</Example>
Is it possible to unmarshal object in such way using JAXB XJC? ( I know about XmlPath in EclipseLink, but can't use it).
You could leverage an XmlAdapter for this use case. In that XmlAdapter you will convert a String to/from an object that has one property mapped to an XML attribute.
XmlAdapter
package forum12914382;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class MyFieldAdapter extends XmlAdapter<MyFieldAdapter.AdaptedMyField, String> {
#Override
public String unmarshal(AdaptedMyField v) throws Exception {
return v.value;
}
#Override
public AdaptedMyField marshal(String v) throws Exception {
AdaptedMyField amf = new AdaptedMyField();
amf.value = v;
return amf;
}
public static class AdaptedMyField {
#XmlAttribute
public String value;
}
}
Example
package forum12914382;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement(name="Example")
#XmlAccessorType(XmlAccessType.FIELD)
class Example{
#XmlJavaTypeAdapter(MyFieldAdapter.class)
String myField;
}
Demo
package forum12914382;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Example.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum12914382/input.xml");
Example example = (Example) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(example, System.out);
}
}
input.xml/Output
<Example>
<myField value="someValue" />
</Example>
Related Example
JAXB Element mapping
Yes, manually add the #XmlAttribute-Annotation or generate the classes from an XSD.

XmlAdapter not working as expected in JAXB RI

I am trying to implement a XmlAdapter for modifying the marshalling/unmarshalling of certain object properties. Particularly, I tried with the NullStringAdapter described here:
Jaxb: xs:attribute null values
The objective of the NullStringAdapter is marshalling null values as empty strings, and viceversa.
The only difference with the example described above and my code, is that I want to apply the adapter to an element, not to an attribute, so what I have is:
#XmlElement
#XmlJavaTypeAdapter(NullStringAdapter.class)
public String getSomeValue() {
return someValue; //someValue could be null, in that case the adapter should marshall it as an empty string
}
However, after some debugging, I realized that the Adapter methods are never called during the marshalling from Java to XML!. This occurs when the XmlElement value is null.
When this value is different than null, the adapter methods are called as expected.
Thanks for any help!.
Note: I'm the EclipseLink JAXB (MOXy) lead, and a member of the JAXB 2 (JSR-222) expert group.
However, after some debugging, I realized that the Adapter methods are
never called during the marshalling from Java to XML!. This occurs
when the XmlElement value is null. When this value is different than
null, the adapter methods are called as expected.
This behaviour varies between implementations of JAXB. The JAXB reference implementation will not call the marshal method on the XmlAdapter when the field/property is null, but MOXy will.
What the JAXB spec says (section 5.5.1 Simple Property)
The get or is method returns the property’s value as specified in the
previous subsection. If null is returned, the property is considered
to be absent from the XML content that it represents.
The MOXy interpretation of this statement is that the value of the field/property is really the value once it has gone through the XmlAdapter. This is necessary to support the behaviour that Sergio is looking for.
Of course the adapter will never be called if there isn't any element in the input to trigger that action. What happens in that example you're linked is that an attribute with an empty value is presented:
<element att="" />
The key here is that there is an att attribute, but it has an empty String. So a JAXB unmarshaller is gonna present that to the setter. But, since there's an adapter declared on it, it will pass through there and get turned into a null value.
But if you had this
<element />
it's another story. There's no att attribute, so the setter would never need to be called.
There's a difference between an element that occurs but has no content and a complete absence of an element. The former can basically be considered to contain an empty String, but the latter is just "not there".
EDIT: tested with these classes...
Bean.java
package jaxbadapter;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name="Test")
public class Bean {
#XmlElement
#XmlJavaTypeAdapter(NullStringAdapter.class)
private String someValue;
public Bean() {
}
public String getSomeValue() {
return someValue;
}
public void setSomeValue(final String someValue) {
this.someValue = someValue;
}
}
NullStringAdapter.java
package jaxbadapter;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class NullStringAdapter extends XmlAdapter<String, String> {
#Override
public String unmarshal(final String v) throws Exception {
if("".equals(v)) {
return null;
}
return v;
}
#Override
public String marshal(final String v) throws Exception {
if(null == v) {
return "";
}
return v;
}
}
ObjectFactory.java
package jaxbadapter;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.namespace.QName;
#XmlRegistry
public class ObjectFactory {
public ObjectFactory() {
}
public Bean createBean() {
return new Bean();
}
#XmlElementDecl(name = "Test")
public JAXBElement<Bean> createTest(Bean value) {
return new JAXBElement<>(new QName("Test"), Bean.class, null, value);
}
}
Main.java
package jaxbadapter;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import javax.xml.transform.stream.StreamResult;
public class Main {
public static void main(String[] args) throws Exception {
final JAXBContext context = JAXBContext.newInstance("jaxbadapter");
final Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
final ObjectFactory of = new ObjectFactory();
final Bean b1 = new Bean();
final Bean b2 = new Bean();
b2.setSomeValue(null);
final Bean b3 = new Bean();
b3.setSomeValue("");
m.marshal(of.createTest(b1), System.out);
System.out.println("");
m.marshal(of.createTest(b2), System.out);
System.out.println("");
m.marshal(of.createTest(b3), System.out);
System.out.println("");
}
}
This is the output:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Test/>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Test/>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Test>
<someValue></someValue>
</Test>
Actually surprised me quite a bit. I've then tried changing the getter to return someValue == null ? "" : someValue; to no avail. Then set a breakpoint on the getter and found out it never gets called.
Apparently JAXB uses reflection to try and retrieve the value rather than going through the setter when using XmlAccessType.FIELD. Hardcore. Now, you can bypass this by using XmlAccessType.PROPERTY instead and annotating either the getter or setter...
package jaxbadapter;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlAccessorType(XmlAccessType.PROPERTY)
#XmlType(name="Test")
public class Bean {
private String someValue;
public Bean() {
}
#XmlElement
#XmlJavaTypeAdapter(NullStringAdapter.class)
public String getSomeValue() {
return someValue;
}
public void setSomeValue(final String someValue) {
this.someValue = someValue;
}
}
... but that still didn't help. The adapter's marshal method was only called once, on the last test case where an empty String had been set. Apparently it first calls the getter and when that returns null, it simply skips the adapter stuff altogether.
The only solution I can come up with is just foregoing the use of an adapter altogether here and put the substitution in the getter, making sure to use XmlAccessType.PROPERTY:
package jaxbadapter;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlAccessorType(XmlAccessType.PROPERTY)
#XmlType(name="Test")
public class Bean {
private String someValue;
public Bean() {
}
#XmlElement
// #XmlJavaTypeAdapter(NullStringAdapter.class)
public String getSomeValue() {
return someValue == null ? "" : someValue;
}
public void setSomeValue(final String someValue) {
this.someValue = someValue;
}
}
That worked for me. It's only really an option if you're creating the JAXB-annotated classes yourself and not generating them via XJC from a schema, though.
Maybe someone can clarify a bit why adapters are skipped for nulls and if there's a way to change that behaviour.

JAXB location in file for unmarshalled objects

I have some objects being unmarshalled from an XML file by JAXB. Is it possible to have JAXB tell me or somehow find out where in the XML file (line and column) each object comes from?
This information is available at some point, because JAXB gives it to me during schema validation errors. But I would like to have it available for validated objects too.
You could do this in JAXB by leveraging an XMLStreamReader and an Unmarshaller.Listener:
Demo
package forum383861;
import java.io.FileInputStream;
import java.util.HashMap;
import java.util.Map;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.Unmarshaller.Listener;
import javax.xml.stream.Location;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Customer.class);
XMLInputFactory xif = XMLInputFactory.newFactory();
FileInputStream xml = new FileInputStream("src/forum383861/input.xml");
XMLStreamReader xsr = xif.createXMLStreamReader(xml);
Unmarshaller unmarshaller = jc.createUnmarshaller();
LocationListener ll = new LocationListener(xsr);
unmarshaller.setListener(ll);
Customer customer = (Customer) unmarshaller.unmarshal(xsr);
System.out.println(ll.getLocation(customer));
System.out.println(ll.getLocation(customer.getAddress()));
}
private static class LocationListener extends Listener {
private XMLStreamReader xsr;
private Map<Object, Location> locations;
public LocationListener(XMLStreamReader xsr) {
this.xsr = xsr;
this.locations = new HashMap<Object, Location>();
}
#Override
public void beforeUnmarshal(Object target, Object parent) {
locations.put(target, xsr.getLocation());
}
public Location getLocation(Object o) {
return locations.get(o);
}
}
}
input.xml
<?xml version="1.0" encoding="UTF-8"?>
<customer>
<address/>
</customer>
Output
[row,col {unknown-source}]: [2,1]
[row,col {unknown-source}]: [3,5]
Customer
package forum383861;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Customer {
private Address address;
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
Address
package forum383861;
public class Address {
}
For More Information
http://blog.bdoughan.com/2011/08/using-unmarshallerlistener-to-capture.html
I'm afraid not. JAXB builds on top of a XML parser, this one will have built up a logical representation of your XML document forgetting the original string representation of your document.
The validation step is done while your string is still read in, so your parser is able to give you an error message telling you the position of the error. JAXB will only bypass that error message. But as soon as the XML is validated and parsed, only the logical representation will exist.

How to add an XML namespace (xmlns) when serializing an object to XML

I'm serializing Objects to XML with the help of XStream.
How do I tell XStream to insert an xmlns to the XML output of my object?
As an example, I have this simple object I want to serialize:
#XStreamAlias(value="domain")
public class Domain
{
#XStreamAsAttribute
private String type;
private String os;
(...)
}
How do I achieve exactly the following output with XStream?
<domain type="kvm" xmlns:qemu="http://libvirt.org/schemas/domain/qemu/1.0">
<os>linux</os>
</domain>
XStream doesn't support namespaces but the StaxDriver it uses, does. You need to set the details of your namespace into a QNameMap and pass that into the StaxDriver:
QNameMap qmap = new QNameMap();
qmap.setDefaultNamespace("http://libvirt.org/schemas/domain/qemu/1.0");
qmap.setDefaultPrefix("qemu");
StaxDriver staxDriver = new StaxDriver(qmap);
XStream xstream = new XStream(staxDriver);
xstream.autodetectAnnotations(true);
xstream.alias("domain", Domain.class);
Domain d = new Domain("kvm","linux");
String xml = xstream.toXML(d);
Output:
<qemu:domain type="kvm" xmlns:qemu="http://libvirt.org/schemas/domain/qemu/1.0">
<qemu:os>linux</qemu:os>
</qemu:domain>
Alternatively, this use case could be handled quite easily with a JAXB implementation (Metro, EclipseLink MOXy, Apache JaxMe, etc):
Domain
package com.example;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Domain
{
private String type;
private String os;
#XmlAttribute
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getOs() {
return os;
}
public void setOs(String os) {
this.os = os;
}
}
package-info
#XmlSchema(xmlns={
#XmlNs(
prefix="qemu",
namespaceURI="http://libvirt.org/schemas/domain/qemu/1.0")
})
package com.example;
import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlSchema;
Demo
package com.example;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
public class Demo {
public static void main(String[] args) throws JAXBException {
JAXBContext jc = JAXBContext.newInstance(Domain.class);
Domain domain = new Domain();
domain.setType("kvm");
domain.setOs("linux");
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(domain, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<domain xmlns:qemu="http://libvirt.org/schemas/domain/qemu/1.0" type="kvm">
<os>linux</os>
</domain>
For More Information
http://bdoughan.blogspot.com/2010/08/jaxb-namespaces.html
http://bdoughan.blogspot.com/2010/10/how-does-jaxb-compare-to-xstream.html
This is a bit of a hack, but it's quick and easy: add a field to your class called xmlns, and only have it non-null during serialisation. To continue your example:
#XStreamAlias(value="domain")
public class Domain
{
#XStreamAsAttribute
private String type;
private String os;
(...)
#XStreamAsAttribute
#XStreamAlias("xmlns:qemu")
String xmlns;
public void serialise(File path) {
XStream xstream = new XStream(new DomDriver());
xstream.processAnnotations(Domain.class);
(...)
PrintWriter out = new PrintWriter(new FileWriter(path.toFile()));
xmlns = "http://libvirt.org/schemas/domain/qemu/1.0";
xstream.toXML(this, out);
xmlns = null;
}
}
To be complete, setting xmlns = null should be in a finally clause. Using a PrintWriter also allows you to insert an XML declaration at the start of the output, if you like.

Categories

Resources