Why is the ObjectFactory not used during unmarshalling?

前端 未结 1 1344
我在风中等你
我在风中等你 2020-12-11 19:17

I have defined the following ObjectFactory:

@XmlRegistry
public class ObjectFactory {

    public Dogs createDogs() {
        return new Dogs();         


        
相关标签:
1条回答
  • 2020-12-11 19:50

    The short answer is because the factory methods are not generated into the @XmlType annotation to tell JAXB to do so:

    @XmlRootElement(name = "listOfDogs")
    @XmlType(factoryClass=ObjectFactory.class, factoryMethod="createDogs") // not generated
    public class Dogs {
    

    Shouldn't it be? Or is ist just a dummy to hold the @XmlRegistry/@XmlElementDecl declarations?

    In my opinion yes it should be used to instantiate the classes.

    ObjectFactory is a throw back to JAXB 1.0. In JAXB 1.0 the spec defined what the generated interfaces looked like and implementations could back those generated interfaces with what ever impl they wanted to provide. Back then you needed to use the ObjectFactory class to create your model in a vendor independent way.

    JAXB 2.0 switched to a POJO model where you were free to use the default constructor. If JAXB 1.0 had never existed would there be an ObjectFactory class, that's hard to tell. Since it previously existed the ObjectFactory class was kept for a couple of reasons:

    1. It made it easier for people transitioning for people transitioning from JAXB 1.0 to interact with the generated model.
    2. It provided a location to specify the multiple root elements for a class via @XmlElementDecl. The @XmlRegistry annotation is really just a marker annotation used to indicate the class that contains the @XmlElementDecl annotations without restricting it to a class called ObjectFactory.

    Your Use Case

    Your use case may be able to be achieved with an XmlAdapter, although its not clear to me what logic you are trying to have in the ObjectFactory.

    XmlAdapter (DogAdapter)

    Your custom logic goes on the XmlAdapter.

    import javax.xml.bind.*;
    import javax.xml.bind.annotation.adapters.*;
    
    public class DogAdapter extends XmlAdapter<JAXBElement<DogType>, JAXBElement<DogType>> {
    
        @Override
        public JAXBElement<DogType> unmarshal(JAXBElement<DogType> v) throws Exception {
            return new Dog(v.getName().getLocalPart(), v.getValue());
        }
    
        @Override
        public JAXBElement<DogType> marshal(JAXBElement<DogType> v) throws Exception {
            return v;
        }
    
    }
    

    Dogs

    The XmlAdapter is referenced from the @XmlJavaTypeAdapter annotation.

    import java.util.*;
    import javax.xml.bind.*;
    import javax.xml.bind.annotation.*;
    import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
    
    @XmlRootElement(name = "listOfDogs")
    public class Dogs {
    
        private List<JAXBElement<DogType>> dogs = new LinkedList<JAXBElement<DogType>>();
    
        @XmlElementWrapper(name = "dogs")
        @XmlElementRef(name = "dog")
        @XmlJavaTypeAdapter(DogAdapter.class)
        public List<JAXBElement<DogType>> getDogs() {
            return this.dogs;
        }
    
        @Override
        public String toString() {
            return "Dogs [dogs=" + dogs + "]";
        }
    
    }
    

    ObjectFactory

    ObjectFactory is now a dumb class that just holds the @XmlElementDecl annotations:

    import javax.xml.bind.*;
    import javax.xml.bind.annotation.*;
    import javax.xml.namespace.QName;
    
    @XmlRegistry
    public class ObjectFactory {
    
        public Dogs createDogs() {
            return new Dogs();
        }
    
        @XmlElementDecl(name = "dog")
        public JAXBElement<DogType> createDog(DogType value) {
            return new Dog(value);
        }
    
        @XmlElementDecl(name = "fido", substitutionHeadName = "dog", substitutionHeadNamespace = "")
        public JAXBElement<DogType> createFido(DogType value) {
            return new JAXBElement<DogType>(new QName("fido"), DogType.class, value);
        }
    
        @XmlElementDecl(name = "barks", substitutionHeadName = "dog", substitutionHeadNamespace = "")
        public JAXBElement<DogType> createBarks(DogType value) {
            return new JAXBElement<DogType>(new QName("barks"), DogType.class, value);
        }
    
    }
    

    UPDATE

    My question, however is more about the specification. According to the spec, should the create* methods from the ObjectFactory be executed or not?

    In JAXB 2 there is no difference in a model created from scratch versus one generated from an XML Schema. As such you need to look to the spec at what it says about classes. According to what is reference below it comes down to no-arg constructor or a specified factory method.

    From section 8.7.1.2 Mapping of the JAXB 2.2 (JSR-222) specification:

    a class must have a public or protected no-arg constructor or a factory method identified by {factoryClass(), factoryMethod()} unless it is adapted using @XmlJavaTypeAdapter.

    0 讨论(0)
提交回复
热议问题