mapping two xmls with different rootelement name to the same java object - java

I have
xml1:
<abc><name>hello</name></abc>
xml2
<xyz><name>hello</name></xyz>
I have one java class.
#XmlRootElement(name="abc") (this
public class Foo{
#XmlElement
String name;
}
I do not want another class, but would like to accomodate xml2 with the Foo class itself.
I'm okay to intercept or modify it during pre-marshalling/pre-unmarshalling.
Thanks!
}

Depending on exactly what you mean by "I don't want another class", maybe this will work out for you:
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import java.io.StringReader;
public class JaxbBindTwoRootElementsToSameClass {
public static void main(String[] args) throws Exception {
String xml1 = "<abc><name>hello</name></abc>";
String xml2 = "<xyz><name>hello</name></xyz>";
Unmarshaller unmarshaller = JAXBContext.newInstance(Foo.class).createUnmarshaller();
Object o1 = unmarshaller.unmarshal(new StringReader(xml1));
Object o2 = unmarshaller.unmarshal(new StringReader(xml2));
System.out.println(o1);
System.out.println(o2);
}
#XmlSeeAlso({Foo.Foo_1.class, Foo.Foo_2.class})
static class Foo {
#XmlRootElement(name = "abc")
static class Foo_1 extends Foo {}
#XmlRootElement(name = "xyz")
static class Foo_2 extends Foo {}
#XmlElement
String name;
#Override
public String toString() {
return "Foo{name='" + name + '\'' + '}';
}
}
}
Output:
Foo{name='hello'}
Foo{name='hello'}
It has the benefit of using JAXB almost exactly the way you usually would. It's just a slightly unconventional class organization. You even only have to pass Foo.class to the JAXBContext when you create it. No tinkering with JAXB internals needed.

Unmarshalling
You could use the unmarshal methods that take a class parameter. When a class is specified the JAXB implementation does not need to use the root element to determine the class to unmarshal to.
Marshalling
When marshaling you can wrap the root object in a JAXBElement to provide the root element information.

Related

Avoid creation of object wrapper type/value in MOXy (JAXB+JSON)

I'm using MOXy 2.6 (JAXB+JSON).
I want ObjectElement and StringElement to be marshalled the same way, but MOXy creates wrapper object when fields are typed as Object.
ObjectElement.java
public class ObjectElement {
public Object testVar = "testValue";
}
StringElement.java
public class StringElement {
public String testVar = "testValue";
}
Demo.java
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import org.eclipse.persistence.jaxb.JAXBContextFactory;
import org.eclipse.persistence.jaxb.MarshallerProperties;
import org.eclipse.persistence.oxm.MediaType;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContextFactory.createContext(new Class[] { ObjectElement.class, StringElement.class }, null);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(MarshallerProperties.MEDIA_TYPE, MediaType.APPLICATION_JSON);
System.out.println("ObjectElement:");
ObjectElement objectElement = new ObjectElement();
marshaller.marshal(objectElement, System.out);
System.out.println();
System.out.println("StringElement:");
StringElement stringElement = new StringElement();
marshaller.marshal(stringElement, System.out);
System.out.println();
}
}
When launching Demo.java, here's the output ...
ObjectElement:
{"testVar":{"type":"string","value":"testValue"}}
StringElement:
{"testVar":"testValue"}
How to configure MOXy/JaxB to make ObjectElement render as StringElement object ? How to avoid the creation of object wrapper with "type" and "value" properties ?
you can use the Annotation javax.xml.bind.annotation.XmlAttribute. This will render ObjectElement and StringElement to the same output.
See the following example:
import javax.xml.bind.annotation.XmlAttribute;
public class ObjectElement {
#XmlAttribute
public Object testVar = "testValue";
}
I've used the following test class to verify the correct behaviour.
EDIT after the question was updated:
Yes it's possible. Instead of using XmlAttribute like before, I switched to javax.xml.bind.annotation.XmlElement in combination with a type attribute.
The class is now declared as:
public class ObjectElement {
#XmlElement(type = String.class)
public Object testVar = "testValue";
}

#XmlAnyElement does not unmarshal into specific Java type, but stop at JAXBElement

To learn how to use #XmlAnyElement, I created the following test service:
#WebService(serviceName = "TestServices")
#Stateless()
public class TestServices {
#WebMethod(operationName = "testMethod")
public ServiceResult testMethod() {
ServiceResult result = new ServiceResult();
result.addObject(new SimpleObj(1, 2));
result.addObject(new SimpleObj(3, 4));
return result;
}
}
SimpleObj is a simple class with 2 int fields. Below is the code for the ServiceResult class:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlSeeAlso({SimpleObj.class})
public class ServiceResult {
#XmlAnyElement(lax = true)
private List<Object> body;
public void addObject(Object objToAdd) {
if (this.body == null)
this.body = new ArrayList();
this.body.add(objToAdd);
}
// Getters and Setters
}
To consume the above service, I created an appclient with the following Main class:
public class Main {
#WebServiceRef(wsdlLocation = "META-INF/wsdl/localhost_8080/TestServices/TestServices.wsdl")
private static TestServices_Service service;
private static TestServices port;
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
port = service.getAdminServicesPort();
ServiceResult result = port.testMethod();
for (Object o : result.getAny()) {
System.out.println("TEST: " + o);
}
}
}
Based on the documentation, with #XmlAnyElement, the unmarshaller will eagerly unmarshal this element to a JAXB object. However, what I observed is that JAXB only parsed my object into JAXBElement instead of going all the way into SimpleObj.
I'd be extremely grateful if you could show me how I can get SimpleObj out of the ServiceResult.
UPDATE:
Below is the SimpleObj class:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class SimpleObj {
private int a;
private int b;
public SimpleObj() {}
public SimpleObj(int a, int b) {
this.a = a;
this.b = b;
}
// Getters and Setters
}
I am unable to reproduce the issue that you are seeing. Below is some demo code that interacts directly with JAXB.
import java.io.*;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(ServiceResult.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
StringReader xml = new StringReader("<serviceResult><simpleObj/><simpleObj/></serviceResult>");
ServiceResult result = (ServiceResult) unmarshaller.unmarshal(xml);
for(Object item : result.getBody()) {
System.out.println(item.getClass());
}
}
}
The output from running the demo code shows that it is instances of SimpleObj in the field annotated with #XmlAnyElement(lax=true).
class forum27871349.SimpleObj
class forum27871349.SimpleObj
UPDATE #1
On the side note, I've read your blog articles on #XmlAnyElement and
I've never seen you had to include #XmlSeeAlso({SimpleObj.class}) in
any of your examples.
I'm not sure why I never leverage #XmlSeeAlso in my examples.
However, in my case, if I don't have this, I would have the error
saying Class *** nor any of its super class is known to this context.
It'd be great if you could also show me if there is a way to make all
of these classes known to the consumer without using #XmlSeeAlso
When you are creating the JAXBContext yourself, you simply need to include anything you would have referenced in an #XmlSeeAlso annotation as part of the classes you used to bootstrap the JAXBContext.
JAXBContext jc = JAXBContext.newInstance(ServiceResult.class, SimpleObj.class);
In a JAX-WS (or JAX-RS) setting where you don't have direct access to the JAXBContext I would recommend using the #XmlSeeAlso annotation like you have done.
UPDATE #2
Regarding the #XmlAnyElement, from the documentation, I thought if the
unmarshaller cannot unmarshal elements into JAXB objects or
JAXBElement objects, I will at least get a DOM node.
When you have a property mapped with #XmlAnyElement(lax=true) the following will happen:
If the element corresponds to the #XmlRootElement of a class, then you will get an instance of that class.
If the element corresponds to the #XmlElementDecl of a class on the ObjectFactory or another class annotated with #XmlRegistry then you will get an instance of that class wrapped in an instance of JAXBElement.
If JAXB does not have an association between the element and a class, then it will convert it to a DOM Element.
I will demonstrate below with an example.
ObjectFactory
import javax.xml.namespace.QName;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
#XmlRegistry
public class ObjectFactory {
#XmlElementDecl(name="simpleObjJAXBElement")
public JAXBElement<SimpleObj> createSimpleObj(SimpleObj simpleObj) {
return new JAXBElement<SimpleObj>(new QName("simpleObjJAXBElement"), SimpleObj.class, simpleObj);
}
}
Demo
import java.io.*;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(ServiceResult.class, ObjectFactory.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
StringReader xml = new StringReader("<serviceResult><simpleObj/><unmapped/><simpleObjJAXBElement/></serviceResult>");
ServiceResult result = (ServiceResult) unmarshaller.unmarshal(xml);
for(Object item : result.getBody()) {
System.out.println(item.getClass());
}
}
}
Output
class forum27871349.SimpleObj
class com.sun.org.apache.xerces.internal.dom.ElementNSImpl
class javax.xml.bind.JAXBElement

#XmlElement with multiple names

I have a situation here, trying to act as a gateway between two APIs. What I need to do, is:
make a request to an APIa;
parse (marshal) the XML response into an java object;
make little changes to it;
and then give a response in XML (unmarshal) to the other end (APIb).
The thing is that I use the same object to parse the API response and to send the response to the other end.
public class ResponseAPI{
#XmlElement(name="ResponseCode") //I receive <ResponseCode> but I need to send <ResultCode>
private String responseCode;
//getter and setter
}
as the comment says: I receive but I need to send
Is there a way to get this done without having to create another extra class which carries ResultCode?
thanks in advance!
You can try next solution using #XmlElements annotaion
#XmlAccessorType(XmlAccessType.FIELD)
public class ResponseAPI
{
#XmlElements(
{
#XmlElement(name = "ResponseCode"),
#XmlElement(name = "ResultCode")
})
private String responseCode;
// ...
}
In this case both ResponseCode and ResultCode will be used during unmarshalling (xml -> object) and only ResultCode during marshalling (object -> xml).
So you can unmarshall XML like
<responseAPI>
<ResponseCode>404</ResponseCode>
</responseAPI>
After marshalling object will looks like
<responseAPI>
<ResultCode>404</ResultCode>
</responseAPI>
Note:
The answer given by Ilya works but isn't guaranteed to work across all implementations of JAXB or even across versions of a single JAXB implementation. The #XmlElements annotation is useful when the decision of which element to marshal depends on the type of the value (see: http://blog.bdoughan.com/2010/10/jaxb-and-xsd-choice-xmlelements.html). In your use case both the ResponseCode and ResultCode elements correspond to type String, unmarshalling will always work fine, but the choice of which element to output is arbitrary. Some JAXB Impls may have last specified wins, but others could easily have first wins.
You could do the following by leveraging #XmlElementRef.
Java Model
ResponseAPI
We will change the responseCode property from type String to JAXBElement<String>. The JAXBElement allows us to store the element name as well as the value.
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class ResponseAPI{
#XmlElementRefs({
#XmlElementRef(name = "ResponseCode"),
#XmlElementRef(name = "ResultCode")
})
private JAXBElement<String> responseCode;
public JAXBElement<String> getResponseCode() {
return responseCode;
}
public void setResponseCode(JAXBElement<String> responseCode) {
this.responseCode = responseCode;
}
}
ObjectFactory
The #XmlElementRef annotations we used on the ResponseAPI class correspond to #XmlElementDecl annotations on a class annotated with #XmlRegistry. Traditionally this class is called ObjectFactory but you can call it anything you want.
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
import javax.xml.namespace.QName;
#XmlRegistry
public class ObjectFactory {
#XmlElementDecl(name="ResponseCode")
public JAXBElement<String> createResponseCode(String string) {
return new JAXBElement<String>(new QName("ResponseCode"), String.class, string);
}
#XmlElementDecl(name="ResultCode")
public JAXBElement<String> createResultCode(String string) {
return new JAXBElement<String>(new QName("ResultCode"), String.class, string);
}
}
Demo Code
input.xml
<responseAPI>
<ResponseCode>ABC</ResponseCode>
</responseAPI>
Demo
When creating the JAXBContext we need to ensure that we include the class that contains the #XmlElementDecl annotations.
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(ResponseAPI.class, ObjectFactory.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("Scratch/src2/forum24554789/input.xml");
ResponseAPI responseAPI = (ResponseAPI) unmarshaller.unmarshal(xml);
ObjectFactory objectFactory = new ObjectFactory();
String responseCode = responseAPI.getResponseCode().getValue();
JAXBElement<String> resultCodeJAXBElement = objectFactory.createResultCode(responseCode);
responseAPI.setResponseCode(resultCodeJAXBElement);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(responseAPI, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<responseAPI>
<ResultCode>ABC</ResultCode>
</responseAPI>

Is there any Java support for JSPON serialization with references?

I'm looking for a Java JSPON serializer that can handle references according to the JSPON specification.
Are there any available that can do this currently? Or is there any way to modify an existing serializer to handle object refs with the $ref notation?
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.
If you are interested in an object-JSON binding approach, below is how it could be done using MOXy. The example below is based on the example one from the JSPON Core Spec:
http://www.jspon.org/JSPON_Core_Spec.htm
Parent
The Parent class is the domain object that corresponds to the root of the JSON message. It has two fields that are of type Child.
package forum9862100;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public class Parent {
protected Child field1;
protected Child field2;
}
Child
The Child class may be referenced by its key. We will handle this use case with an XmlAdapter. We link to an XmlAdapter via the #XmlJavaTypeAdapter annotation.
package forum9862100;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlJavaTypeAdapter(ChildAdapter.class)
#XmlAccessorType(XmlAccessType.FIELD)
public class Child {
protected String id;
protected String foo;
protected Integer bar;
}
ChildAdapter
Below is the implementation of the XmlAdapter. This XmlAdapter is stateful which means we will need to set an instance on the Marshaller and Unmarshaller.
package forum9862100;
import java.util.*;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class ChildAdapter extends XmlAdapter<ChildAdapter.AdaptedChild, Child>{
private List<Child> childList = new ArrayList<Child>();
private Map<String, Child> childMap = new HashMap<String, Child>();
public static class AdaptedChild extends Child {
#XmlElement(name="$ref")
public String reference;
}
#Override
public AdaptedChild marshal(Child child) throws Exception {
AdaptedChild adaptedChild = new AdaptedChild();
if(childList.contains(child)) {
adaptedChild.reference = child.id;
} else {
adaptedChild.id = child.id;
adaptedChild.foo = child.foo;
adaptedChild.bar = child.bar;
childList.add(child);
}
return adaptedChild;
}
#Override
public Child unmarshal(AdaptedChild adaptedChild) throws Exception {
Child child = childMap.get(adaptedChild.reference);
if(null == child) {
child = new Child();
child.id = adaptedChild.id;
child.foo = adaptedChild.foo;
child.bar = adaptedChild.bar;
childMap.put(child.id, child);
}
return child;
}
}
Demo
The code below demonstrates how to specify a stateful XmlAdapter on the Marshaller and Unmarshaller:
package forum9862100;
import java.io.File;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Parent.class);
StreamSource json = new StreamSource(new File("src/forum9862100/input.json"));
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setProperty("eclipselink.media-type", "application/json");
unmarshaller.setProperty("eclipselink.json.include-root", false);
unmarshaller.setAdapter(new ChildAdapter());
Parent parent = (Parent) unmarshaller.unmarshal(json, Parent.class).getValue();
System.out.println(parent.field1 == parent.field2);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty("eclipselink.media-type", "application/json");
marshaller.setProperty("eclipselink.json.include-root", false);
marshaller.setAdapter(new ChildAdapter());
marshaller.marshal(parent, System.out);
}
}
Output
Below is the output from running the demo code. Note how the two instances of Child pass the identity test.
true
{
"field1" : {
"id" : "2",
"foo" : "val",
"bar" : 4
},
"field2" : {
"$ref" : "2"
}}
For More Information
http://blog.bdoughan.com/2011/09/mixing-nesting-and-references-with.html
http://blog.bdoughan.com/2011/08/json-binding-with-eclipselink-moxy.html
I would use one of the many Object to JSon serialization libraries. Many of the libraries are extensible, but I suspect adding references could get complicated unless you make some pragmatic choices as to when to use these.

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.

Categories

Resources