Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JAXB: Problem deserializing a class B that extends a class A

Please consider the following example:

There is a ClassA and a ClassB which extends it. My problem is now that I have to unmarshall a ClassB from an xml file. Please note that ClassA can not be changed as it is not under my control.

Several problems are noted in this example:

The main problem is that ClassA does not have a default no-arg constructor which is required by JAXB without Adapter. Therefore I implemented MyAdapter which maps ClassB to the simple class ValB which can be processed by JAXB without any problems.

The main problem is how to make JAXB use this adapter? Neither defining the @XmlJavaTypeAdapter on class level nor registering the Adapter to the unmarshaller does it.

Does anybody know how to make JAXB use MyAdapter so that the unmarshaller returns an object that is an instance of ClassA?

public class JaxbTest {

    public static abstract class ClassA {
        public ClassA(String id) {
        }
    }

    @XmlRootElement
    @XmlJavaTypeAdapter(MyAdapter.class) // does not have an effect
    public static class ClassB extends ClassA {

        public String text;

        public ClassB() {
            super("");
        }
    }

    public static class ValB {
        public String text;
    }

    public static class MyAdapter extends XmlAdapter<ValB, ClassB> {

        @Override
        public ClassB unmarshal(ValB v) throws Exception {
            ClassB b = new ClassB();
            b.text = v.text;
            return b;
        }

        @Override
        public ValB marshal(ClassB v) throws Exception {
            ValB b = new ValB();
            b.text = v.text;
            return b;
        }

    }

    public static void main(String[] args) {
        try {
            JAXBContext context = JAXBContext.newInstance(ClassB.class);
            Unmarshaller unmarshaller = context.createUnmarshaller();
            unmarshaller.setAdapter(new MyAdapter()); // does not have an effect
            ClassA a = (ClassA) unmarshaller.unmarshal(new File("test.xml"));
            // do somthing with a
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

BTW: Don't take the code too serious - it is just an example demonstrating the problem. I know that the definition of ClassA and ClassB are not really useful.

like image 206
Robert Avatar asked Dec 16 '10 10:12

Robert


1 Answers

UPDATE

We have addressed this issue in the upcoming EclipseLink JAXB (MOXy) 2.2.0 release (see bug #332742). In this release abstract classes will not be checked for a no-arg constructor.

Pre-release versions with this fix can be obtained here starting December 18th:

  • http://www.eclipse.org/eclipselink/downloads/nightly.php

Workaround

This is what the @XmlTransient annotation is for. If possible do the following:

@XmlTransient
public static abstract class ClassA {
    public ClassA(String id) {
    }
}

If it is not possible to annotate ClassA directly, you could leverage an EclipseLink JAXB (MOXy) extension to do this. MOXy allows you to specify JAXB metadata as an XML file. This is useful when you can't modify a model class:

  • http://bdoughan.blogspot.com/2010/12/extending-jaxb-representing-annotations.html

Below are some articles explaining @XmlAdapter:

  • http://bdoughan.blogspot.com/2010/12/jaxb-and-immutable-objects.html
  • http://bdoughan.blogspot.com/2010/07/xmladapter-jaxbs-secret-weapon.html
like image 121
bdoughan Avatar answered Nov 15 '22 00:11

bdoughan