jaxb-ri
jaxb-ri copied to clipboard
JAXB ignores overridden getter giving XXX is an interface, and JAXB can't handle interfaces
This is my code:
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.Marshaller;
import jakarta.xml.bind.Unmarshaller;
import jakarta.xml.bind.annotation.XmlAttribute;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.annotation.adapters.XmlAdapter;
import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.HashSet;
import java.util.Set;
public class Test {
public static interface Teacher {
String getName();
}
public static class ConcreteTeacher implements Teacher {
private String name;
public ConcreteTeacher() {
}
public ConcreteTeacher(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}
public static class GroupParent {
private Set<Teacher> teachers;
public Set<Teacher> getTeachers() {
return teachers;
}
public void setTeachers(Set<Teacher> teachers) {
this.teachers = teachers;
}
}
@XmlRootElement(name = "group")
public static class Group extends GroupParent {
@Override
@XmlElement(name = "teachers")
@XmlJavaTypeAdapter(TeacherAdapter.class)
public Set<Teacher> getTeachers() {
return super.getTeachers();
}
}
public static class TeacherAdapter extends XmlAdapter<TeacherAdapter.AdaptedTeachers, Set<Teacher>> {
public static class AdaptedTeachers {
@XmlElement(name = "teacher")
public Set<AdaptedTeacher> teacherList = new HashSet<>();
}
public static class AdaptedTeacher {
@XmlAttribute(name = "name")
public String name;
}
@Override
public Set<Teacher> unmarshal(AdaptedTeachers adaptedTeachers) throws Exception {
Set<Teacher> teachers = new HashSet<>();
for (AdaptedTeacher adaptedTeacher : adaptedTeachers.teacherList) {
teachers.add(new ConcreteTeacher(adaptedTeacher.name));
}
return teachers;
}
@Override
public AdaptedTeachers marshal(Set<Teacher> teachers) throws Exception {
AdaptedTeachers adaptedTeachers = new AdaptedTeachers();
for (Teacher teacher : teachers) {
AdaptedTeacher adaptedTeacher = new AdaptedTeacher();
adaptedTeacher.name = teacher.getName();
adaptedTeachers.teacherList.add(adaptedTeacher);
}
return adaptedTeachers;
}
}
private static final String XML_STRING = """
<group>
<teachers>
<teacher name="Alice"/>
<teacher name="Bob"/>
</teachers>
</group>
""";
public static void main(String[] args) {
try {
JAXBContext context = JAXBContext.newInstance(Group.class);
Group group = new Group();
Set<Teacher> teachers = new HashSet<>();
teachers.add(new ConcreteTeacher("Alice"));
teachers.add(new ConcreteTeacher("Bob"));
group.setTeachers(teachers);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
StringWriter writer = new StringWriter();
marshaller.marshal(group, writer);
String xml = writer.toString();
System.out.println("Serialized XML:");
System.out.println(xml);
Unmarshaller unmarshaller = context.createUnmarshaller();
StringReader reader = new StringReader(XML_STRING);
Group deserializedGroup = (Group) unmarshaller.unmarshal(reader);
System.out.println("Deserialized Group:");
for (Teacher teacher : deserializedGroup.getTeachers()) {
System.out.println(teacher.getName());
}
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
If you run it, it will give:
org.glassfish.jaxb.runtime.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
com.foo.mavenproject19.Test$Teacher is an interface, and JAXB can't handle interfaces.
this problem is related to the following location:
at com.foo.mavenproject19.Test$Teacher
at public java.util.Set com.foo.mavenproject19.Test$GroupParent.getTeachers()
at com.foo.mavenproject19.Test$GroupParent
at com.foo.mavenproject19.Test$Group
But, if you update it to
@XmlRootElement(name = "group")
public static class Group {
private Set<Teacher> teachers;
@XmlElement(name = "teachers")
@XmlJavaTypeAdapter(TeacherAdapter.class)
public Set<Teacher> getTeachers() {
return teachers;
}
public void setTeachers(Set<Teacher> teachers) {
this.teachers = teachers;
}
}
Then everything works fine.
The problem of this issue is important in case when you have a class from one project that doesn't use XML and you need to write/read it to/from XML file in another project, so you use inheritance.