JAXB 2.x: Abstract methods get marshalled as Attribute - java

I have an abstract root class, let's say A.
And I have several implementation classes extending A.
A has FIELD annotation as well as some #XmlElement annotated properties.
A also has an abstract method.
When marshalling (B extends A), the value returned by the abstract method gets marshalled as attribute. Not as expected, right?
#XmlAccessorType(XmlAccessType.FIELD)
public abstract class SpecialProfile extends ContentNodeBean {
#XmlElement(name="do-index", namespace="my")
private boolean doIndex = false;
public abstract SpecialProfileType getSpecialProfileType();
... getters and setters for properties ...
}
Does anybody have the same issue and how can this be fixed?
I am using org.eclipse.persistence.moxy 2.1.2

I am attempting to reproduce your issue, but so far have been unsuccessful. Can you see where I'm doing something different than you are? The following is my sample code:
A
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public abstract class A {
public abstract C getC();
public abstract void setC(C c);
}
B
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class B extends A {
private C c;
#Override
public C getC() {
return c;
}
#Override
public void setC(C c) {
this.c = c;
}
}
C
public class C {
}
Demo
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import org.eclipse.persistence.Version;
public class Demo {
public static void main(String[] args) throws Exception {
System.out.println(Version.getVersionString());
JAXBContext jc = JAXBContext.newInstance(B.class);
System.out.println(jc);
B b = new B();
b.setC(new C());
Marshaller marshaller = jc.createMarshaller();
marshaller.marshal(b,System.out);
}
}
Output
2.1.2.v20101206-r8635
org.eclipse.persistence.jaxb.JAXBContext#100ab23
<?xml version="1.0" encoding="UTF-8"?>
<b xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="b"><c/></b>
UPDATE
Based on your comments:
B does not inherit A's XmlAccessorType settings.
It is not the abstract method that you need to mark #XmlTransient, but the field used to implement the accessor on the B class.
The following is what class B should look like:
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class B extends A {
#XmlTransient
private C c;
#Override
public C getC() {
return c;
}
#Override
public void setC(C c) {
this.c = c;
}
}

Related

JAXB Unmarshalling derived types

Based on the following program, it prints out what we expect it to correctly.
Is it possible for such a program to unmarshall correctly if the classes ClassA and ClassB used
the same XmlRootElement name? For example, if they were both defined as "typeA"...? Would it be
possible to do something like that with JAXB?
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.*;
import java.io.ByteArrayOutputStream;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.Date;
public class JaxbABCTest {
public static void main(String[] args) throws JAXBException, UnsupportedEncodingException {
final JAXBContext context = JAXBContext.newInstance(ABC.class);
final Marshaller marshaller = context.createMarshaller();
final Unmarshaller unmarshaller = context.createUnmarshaller();
ABC class1 = new ClassA();
ABC class2 = new ClassB();
final ByteArrayOutputStream baosA = new ByteArrayOutputStream();
final ByteArrayOutputStream baosB = new ByteArrayOutputStream();
// Marshall to XML
marshaller.marshal(class1, baosA);
marshaller.marshal(class2, baosB);
String xmlA = baosA.toString(Charset.defaultCharset().name());
String xmlB = baosB.toString(Charset.defaultCharset().name());
System.out.println(xmlA);
System.out.println(xmlB);
// Now attempt the reverse.
Object unmarshalA = unmarshaller.unmarshal(new StringReader(xmlA));
Object unmarshalB = unmarshaller.unmarshal(new StringReader(xmlB));
System.out.println(unmarshalA.getClass());
System.out.println(unmarshalB.getClass());
}
}
#XmlTransient
#XmlSeeAlso({
ClassA.class,
ClassB.class
})
abstract class ABC {
private int a;
#XmlAttribute
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
}
#XmlRootElement(name = "typeA")
class ClassA extends ABC {
private String b;
private Date c;
#XmlAttribute
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
#XmlAttribute
public Date getC() {
return c;
}
public void setC(Date c) {
this.c = c;
}
}
#XmlRootElement(name = "typeB")
class ClassB extends ABC {
private String b;
private Date c;
private boolean d;
private float e;
#XmlAttribute
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
#XmlAttribute
public Date getC() {
return c;
}
public void setC(Date c) {
this.c = c;
}
#XmlAttribute
public boolean isD() {
return d;
}
public void setD(boolean d) {
this.d = d;
}
#XmlAttribute
public float getE() {
return e;
}
public void setE(float e) {
this.e = e;
}
}
For straightforward JAXB unmarshalling, (root) one element name refers to an element with a specific structure which is mapped to one certain Java class.
You can, of course, define a class containing the union of the fields of A and B - as long as fields occuring in both classes match. You'll have to have some attribute that lets you decide which of the two "subclasses" it really is. (This is similar in spirit to using an XPath expression that includes a test for the presence (or even value) of an attribute or element.)
You may also pursue a more elaborate approach leaving this element unmarshalled after reading the XML, investigate the DOM tree and create a JAXBContext according to what has been detected. This will let you use the same element name with Java types exactly matching the XML content. Of course, an unambiguous criterion must be available, and youÄll have to write the code for analysing based on te raw DOM tree data.

how to know the subclass of mapping in hibernate

#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public class A{
private long id;
}
#Entity
public class B extends A{
private String bProperty;
}
#Entity
public class C extends A{
private String cProperty;
}
#Entity
public class Person{
#OneToMany
private Set<A> a;
}
when I use person.getVehicles
How can I know the A is B or C?
I'm using instanceof to check and cast it to get bProperty or cProperty.
Is there any other better way?
The only safe way is to use a polymorphic method. Even instanceof will not work because the instance might actually be a proxy, i.e. a subclass of A that is neither a B or a C, but delegates to a B or a C.
public class A{
private long id;
public abstract boolean isB();
public abstract boolean isC();
public abstract String getBProperty();
public abstract String getCProperty();
}
public class B extends A{
private String bProperty;
public boolean isB() {
return true;
}
public boolean isC() {
return false;
}
public String getBProperty() {
return bProperty;
}
public String getCProperty() {
throw new IllegalStateException("I'm not a C");
}
}
To be cleaner, try using the visitor pattern. I've written a blog post about it. It's in French, but it should be easily translatable.

EclipseLink and Spring OXM Inheritance issues

I am stuck with the inheritance problem while trying to unmarshal to object. Here is my class
A
#XmlRootElement(name="A")
public abstract class A{
}
B
#XmlRootElement(name="B")
public class B extends A{
String bField;
#XmlAttribute(name="b")
public String getBField(){
return bField;
}
public void setBField(String value){
this.bField = value;
}
}
C
#XmlRootElement(name="C")
public class C extends A{
String cField;
#XmlAttribute(name="c")
public String getCField(){
return cField;
}
public void setCField(String value){
this.cField = value;
}
}
Container
#XmlRootElement(name="container")
public class Container{
ArrayList<B> listB;
ArrayList<C> listC;
public ArrayList<B> getListB(){
return listB;
}
#XmlElementWrapper(name="list-B")
#XmlElement(name="b")
public ArrayList<B> getListB(){
return listB;
}
#XmlElementWrapper(name="list-C")
#XmlElement(name="c")
public ArrayList<C> getListC(){
return listC;
}
public ArrayList<C> getListC(){
return listC;
}
}
Then the input XML file
<container>
<list-B>
<b b="BFied"/>
</list-B>
<list-C>
<c c="CField"/>
</list-C>
</container>
I used EclipseLink JAXB integrated with Spring OXM. When i unmarshal xml file to an instance of Container, every thing is duplicated. In list B i have 2 B instances which duplicated ( the same thing with list C).
Please let me know where did i do wrong? Thank you!
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
I haven't been able to reproduce the issue that you are seeing. I am using the EclipseLink 2.4.0 which can be obtained from the following location:
http://www.eclipse.org/eclipselink/downloads/
Below is my complete code based on your question:
A
package forum11642669;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="A")
public abstract class A{
}
B
package forum11642669;
import javax.xml.bind.annotation.*;
#XmlRootElement(name = "B")
public class B extends A {
String bField;
#XmlAttribute(name = "b")
public String getBField() {
return bField;
}
public void setBField(String value) {
this.bField = value;
}
}
C
package forum11642669;
import javax.xml.bind.annotation.*;
#XmlRootElement(name = "C")
public class C extends A {
String cField;
#XmlAttribute(name = "c")
public String getCField() {
return cField;
}
public void setCField(String value) {
this.cField = value;
}
}
Container
The version of the Container class you had in your question wouldn't compile, so I've modified it below:
package forum11642669;
import java.util.ArrayList;
import javax.xml.bind.annotation.*;
#XmlRootElement(name = "container")
public class Container {
ArrayList<B> listB;
ArrayList<C> listC;
#XmlElementWrapper(name = "list-B")
#XmlElement(name = "b")
public ArrayList<B> getListB() {
return listB;
}
public void setListB(ArrayList<B> listB) {
this.listB = listB;
}
#XmlElementWrapper(name = "list-C")
#XmlElement(name = "c")
public ArrayList<C> getListC() {
return listC;
}
public void setListC(ArrayList<C> listC) {
this.listC = listC;
}
}
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
package forum11642669;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Container.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum11642669/input.xml");
Container container = (Container) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(container, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8"?>
<container>
<list-B>
<b b="BFied"/>
</list-B>
<list-C>
<c c="CField"/>
</list-C>
</container>

JAXB and abstract classes

I'm trying to use JAXB to unmarshall some XML, but I'm getting an "Unable to create an instance of..." exception. I understand why--it's trying to make an instance of an abstract class. What I want is to have it make an instance of a specific implementing class. My goal with this is to have class-specific checks on setter methods. Maybe "qux" is a valid baz value for BarImpl, but BarImpl2 wants to do something else.
I got part of the way there by not annotating Foo, but if I unannotate bar, things get ugly.
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import org.junit.Test;
public class JAXBTest {
#Test
public void test() throws javax.xml.bind.JAXBException {
String xml =
"<foo>" +
" <bar>" +
" <baz>qux</baz>" +
" </bar>" +
"</foo>";
javax.xml.bind.JAXBContext context = javax.xml.bind.JAXBContext.newInstance(
FooImpl.class,
BarImpl.class
);
javax.xml.bind.Unmarshaller unmarshaller = context.createUnmarshaller();
unmarshaller.unmarshal(new java.io.StringReader(xml));
}
#XmlRootElement(name="foo")
public static abstract class Foo {
#XmlElement(name="bar")
Bar bar;
}
#XmlRootElement(name="bar")
public static abstract class Bar {
#XmlElement(name="baz")
String baz;
}
public static class FooImpl extends Foo { }
public static class BarImpl extends Bar { }
}
You could do the following:
Annotation the impl classes with #XmlRootElement instead of the abstract classes.
Mark the abstract classes with #XmlTransient (see http://blog.bdoughan.com/2011/06/ignoring-inheritance-with-xmltransient.html)
Use #XmlElement(type=BarImpl.class) on the bar property to specify the concrete type (see http://blog.bdoughan.com/2011/05/jaxb-and-interface-fronted-models.html).
JAXBTest
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElements;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import org.junit.Test;
public class JAXBTest {
#Test
public void test() throws javax.xml.bind.JAXBException {
String xml =
"<foo>" +
" <bar>" +
" <baz>qux</baz>" +
" </bar>" +
"</foo>";
javax.xml.bind.JAXBContext context = javax.xml.bind.JAXBContext.newInstance(
FooImpl.class,
BarImpl.class
);
javax.xml.bind.Unmarshaller unmarshaller = context.createUnmarshaller();
unmarshaller.unmarshal(new java.io.StringReader(xml));
}
#XmlTransient
public static abstract class Foo {
#XmlElements({
#XmlElement(name="bar",type=BarImpl.class),
#XmlElement(name="bar",type=BarImpl2.class),
})
Bar bar;
}
#XmlTransient
public static abstract class Bar {
#XmlElement(name="baz")
String baz;
}
#XmlRootElement(name="foo")
public static class FooImpl extends Foo { }
#XmlRootElement(name="bar")
public static class BarImpl extends Bar { }
public static class BarImpl2 extends Bar { }
}

Why doesn't JAXB allow annotations on getters that all pull from the same member variable?

Why does example A work, while example B throws a "JAXB annotation is placed on a method that is not a JAXB property" exception?
I'm using JAX-WS with Spring MVC.
Example A
package com.casanosa2.permissions;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
#XmlAccessorType(XmlAccessType.PROPERTY)
#XmlType(name = "FooXMLMapper")
public class FooXMLMapper implements IFoo {
#XmlElement
private final boolean propA;
#XmlElement
private final boolean propB;
public FooMapper(IFoo foo) {
propA = foo.getPropA()
propB = foo.getPropB()
}
public FooMapper() {
propA = false;
propB = false;
}
#Override
public boolean getPropA() {
return propA;
}
#Override
public boolean getPropB() {
return propB;
}
}
Example B
#XmlAccessorType(XmlAccessType.PROPERTY)
#XmlType(name = "FooXMLMapper")
public class FooXMLMapper {
private final IFoo foo;
public FooMapper() {
foo = new IFoo() {
#Override
public boolean getPropA() {
return false;
}
#Override
public boolean getPropB() {
return false;
}
};
}
public FooXMLMapper(IFoo foo) {
this.foo = foo;
}
#XmlElement
public boolean getPropA() {
return foo.getPropA();
}
#XmlElement
public boolean getPropB() {
return foo.getPropB();
}
}
I believe the accessors are ignored if it's looking directly at the instance variables and in your example B there are no actual instance variables of the right name. You have to tell it explicitly to use #XmlAccessorType(XmlAccessType.NONE) on the class and #XmlElement and #XmlAttribute on the get/set methods. At least, that's what I ended up doing with my JAXB mapping.
I believe for it to be a proper JAXB property, you would need setters for them as well as getters. (you would likely need a default constructor as well).
I haven't tried your code yet, but it's example A that looks wrong, not B. In example A you have specified the property accessors (get/set methods) but you have annotated the class fields instead (instance variables).

Categories