JAXB and abstract classes - java

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 { }
}

Related

How can an abstract class return an ArrayList of the subclass object?

I'm trying to return an array list of the subclass object
Suppose
abstract class Foo {
protected abstract ArrayList<Foo> getAlotOfMyself();
};
Is a super class where it's sub-classes need to return an array of themselves,
for example it's sub-class:
class Bar extends Foo{
public ArrayList<Bar> getAlotOfMyself(){
// Do the interesting stuff
}
};
However this doesn't work because of java, ArrayList<Foo> is not the same type as ArrayList<Bar> even if Bar is a subclass of Foo
I've tried changing the ArrayList in Foo to ArrayList<? extends Foo> but it seems to only work as long as Foo is not an abstract class (and therefore getAlotOfMyself() is implemented in Foo), it doesn't compile saying :
cannot convert from ArrayList<capture#1-of ? extends Foo> to ArrayList<Foo>.
The reason the causes that error is this
void interestingFunction(Foo foo){
ArrayList<Foo> alot = foo.getAlotOfMyself(); // the compile error happens here
}
and of course this function is only called on Bar and other sub-classes
Try this and see if it does what you want.
import java.util.ArrayList;
import java.util.List;
public class SubclassStuff {
public static void main(String[] args) {
Bar b = new Bar();
b.interestingFunction(b);
}
}
abstract class Foo {
public String name;
public Foo() {
}
public Foo (String name) {
this.name = name;
}
protected abstract ArrayList<? extends Foo> getAlotOfMyself();
public void interestingFunction(Foo foo) {
System.out.println(foo.getAlotOfMyself());
}
}
class Bar extends Foo {
public ArrayList<Bar> getAlotOfMyself() {
return new ArrayList<>(List.of(new Bar("I am Bar")));
}
public Bar() {
super();
}
public Bar (String name) {
this.name = name;
}
public String toString() {
return name;
}
}

lombok #Builder(toBuilder = true) compilation error when used on constructor of a sub class

My code is as followd
package test.lombok;
import lombok.*;
#AllArgsConstructor(access = AccessLevel.PROTECTED)
#Getter
public class SuperClass {
private int foo;
#Getter
public static class SubClass extends SuperClass {
private int bar;
#Builder(toBuilder = true)
private SubClass(int foo, int bar) {
super(foo);
this.bar = bar;
}
}
}
As showed above, I'm trying to use #Builder(toBuilder = true) on a sub class.
When toBuilder set to false, there is no problem at all.
But when I set toBuilder = true, I got an compilation error "Error:java: foo has private access in test.lombok.SuperClass".
I'm wondering why does this happen and how to fix this.
Lombok tries to create toBuilder method in SubClass when the attribute toBuilder is set to true in annotation #Builder. The method returns the SubClassBuilder class. Here is the what the toBuilder method would look like,
public SuperClass.SubClass.SubClassBuilder toBuilder() {
return (new SuperClass.SubClass.SubClassBuilder())
.foo(this.foo).bar(this.bar);
}
As you notice, toBuilder method tries to access foo attribute directly and not by method getFoo. Since foo is private and belongs to the parent class, SuperClass, you get the following error:
Error:java: foo has private access in test.lombok.SuperClass
The problem is because of how the toBuilder method is implemented in SubClass:
public SuperClass.SubClass.SubClassBuilder toBuilder() {
return (new SuperClass.SubClass.SubClassBuilder()).foo(this.foo).bar(this.bar);
}
Instead of this.foo it should be super.foo, and the code would compile. Accessing super.foo is possible in this case because SubClass is an inner class of the SuperClass, otherwise, Java would disallow super.foo too.
If you want to see the code generated by lombok by for yourself, declare foo as public, then compile, then delombok (or decompile) and you will see code like this (then change the property to private to see where the error occurs):
import java.beans.ConstructorProperties;
public class SuperClass {
public int foo;
#ConstructorProperties({"foo"})
protected SuperClass(int foo) {
this.foo = foo;
}
public int getFoo() {
return this.foo;
}
public static class SubClass extends SuperClass {
private int bar;
private SubClass(int foo, int bar) {
super(foo);
this.bar = bar;
}
public static SuperClass.SubClass.SubClassBuilder builder() {
return new SuperClass.SubClass.SubClassBuilder();
}
public SuperClass.SubClass.SubClassBuilder toBuilder() {
return (new SuperClass.SubClass.SubClassBuilder()).foo(this.foo).bar(this.bar);
}
public int getBar() {
return this.bar;
}
public static class SubClassBuilder {
private int foo;
private int bar;
SubClassBuilder() {
}
public SuperClass.SubClass.SubClassBuilder foo(int foo) {
this.foo = foo;
return this;
}
public SuperClass.SubClass.SubClassBuilder bar(int bar) {
this.bar = bar;
return this;
}
public SuperClass.SubClass build() {
return new SuperClass.SubClass(this.foo, this.bar);
}
public String toString() {
return "SuperClass.SubClass.SubClassBuilder(foo=" + this.foo + ", bar=" + this.bar + ")";
}
}
}
}
EDIT: Thanks to #maaartinus for pointing me to super.foo, the answer is updated with that info.
AFAICT this is a Lombok bug. There are three ways, how to access foo and only one of them works:
plain foo leads to "Cannot make a static reference to the non-static field foo"
this.foo as used by Lombok leads to "The field SuperClass.foo is not visible"
super.foo works!
AFAIK everything declared in the same source file is accessible somehow, but finding the proper expression might be tricky.
Starting from Lombok 1.18.12, you can use the new, experimental feature #SuperBuilder.
It supports toBuilder :
import lombok.*;
import lombok.experimental.SuperBuilder;
#AllArgsConstructor(access = AccessLevel.PROTECTED)
#Getter
#ToString // For demonstration purposes only; only used in the main method.
#SuperBuilder(toBuilder = true)
public class SuperClass {
private int foo;
#Getter
#ToString(callSuper = true) // For demonstration purposes only; only used in the main method.
#SuperBuilder(toBuilder = true)
public static class SubClass extends SuperClass {
private int bar;
private SubClass(int foo, int bar) {
super(foo);
this.bar = bar;
}
}
public static void main(String[] argv) {
SubClass sc = SubClass.builder()
.foo(1)
.bar(2)
.build();
System.out.println(sc);
}
}
Prints:
SuperClass.SubClass(super=SuperClass(foo=1), bar=2)

JAXB abstract class with #XmlTransient

I'm using JAXB to unmarshal some xml into an object(s).
I have a class which inherit from an abstract class. I've marked the abstract class as #XmlTransient. Then using XMLType PropOrder I can access the properties in the abstract class like so:
#XmlType( propOrder = { "id"...
Cool. Problem is sometimes it isn't an element that I want to access but rather an attribute. Normally you would define such a property using #XMLAttribute to indicate the value is stored in an xml attribute and not an element. But given the fact that I've already used XMLTransient on the abstract class where 'id' is defined, JAXB complains when I try to mark the field as #XMLAttribute.
JAXB is complaining that I'm trying to access/return two fields of with the same name.
Can anyone please point me in the right direction? I'm building for GAE so I dn't really want to use any other libraries.
Thanks in advance!
Below are a couple of things you can do:
Java Model
Foo
You can annotate the property on the parent class with #XmlAttribute.
import javax.xml.bind.annotation.*;
#XmlTransient
public class Foo {
private String att1;
private String att2;
#XmlAttribute
public String getAtt1() {
return att1;
}
public void setAtt1(String att1) {
this.att1 = att1;
}
public String getAtt2() {
return att2;
}
public void setAtt2(String att2) {
this.att2 = att2;
}
}
Bar
You can override the property on the subclass and annotate it with #XmlAttribute.
import javax.xml.bind.annotation.*;
#XmlRootElement
public class Bar extends Foo {
#Override
#XmlAttribute
public String getAtt2() {
return super.getAtt2();
}
#Override
public void setAtt2(String att2) {
super.setAtt2(att2);
}
}
Demo Code
Demo
Here is some demo code you can run to show that everything works.
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Bar.class);
Bar bar = new Bar();
bar.setAtt1("a");
bar.setAtt2("b");
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(bar, System.out);
}
}
Output
Below is the output from running the demo code:
<?xml version="1.0" encoding="UTF-8"?>
<bar att1="a" att2="b"/>

JAXB 2.x: Abstract methods get marshalled as Attribute

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

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