Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MOXY ignores overridden getter containing collection with interface giving errors #2239

Open
PavelTurk opened this issue Aug 15, 2024 · 0 comments

Comments

@PavelTurk
Copy link

PavelTurk commented Aug 15, 2024

Describe the bug
MOXY ignores overridden getter containing collection with interface giving errors

To Reproduce
If you run this 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();
        }
    }
}

You will get this exceptions:

jakarta.xml.bind.UnmarshalException
 - with linked exception:
[Exception [EclipseLink-63] (Eclipse Persistence Services - 4.0.4.v202407190748-059428cdd2583c46f1f3e50d235854840a6fa9a7): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: The instance creation method [com.foo.mavenproject19.Test$Teacher.<Default Constructor>], with no parameters, does not exist, or is not accessible.
Internal Exception: java.lang.NoSuchMethodException: com.foo.mavenproject19.Test$Teacher.<init>()
Descriptor: XMLDescriptor(com.foo.mavenproject19.Test$Teacher --> [])]
	at org.eclipse.persistence.jaxb.JAXBUnmarshaller.unmarshal(JAXBUnmarshaller.java:230)
	at com.foo.mavenproject19.Test.main(Test.java:138)
Caused by: Exception [EclipseLink-63] (Eclipse Persistence Services - 4.0.4.v202407190748-059428cdd2583c46f1f3e50d235854840a6fa9a7): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: The instance creation method [com.foo.mavenproject19.Test$Teacher.<Default Constructor>], with no parameters, does not exist, or is not accessible.
Internal Exception: java.lang.NoSuchMethodException: com.foo.mavenproject19.Test$Teacher.<init>()
Descriptor: XMLDescriptor(com.foo.mavenproject19.Test$Teacher --> [])

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.

  • EclipseLink version - 4.0.4
  • Java/JDK version - 22

GlassFish jaxb also can't process such code - see eclipse-ee4j/jaxb-ri#1811

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant