JAXB marshalling purely from interfaces

前端 未结 2 1599
甜味超标
甜味超标 2021-02-13 14:05

I have a complex hierarchy of Java interfaces that I\'d like to marshal (and not necessarily unmarshal) with JAXB. These interfaces represent objects that will be returned from

2条回答
  •  Happy的楠姐
    2021-02-13 15:06

    Short answer: use @XmlElement(type = Object.class) on your interface field.

    Details below:

    I have found 2 ways in which you could make JAXB serialize your interfaces:

    1. @XmlAnyElement
    2. @XmlElement(type = Object.class)

    1.@XmlAnyElement

    Simply annotate your interface type field with @XmlAnyElement and JAXB will serialize the interface from it's concrete type. Don't forget to annotate the concrete types with @XmlRootElement and to add the concrete types to the JAXBContext. Full example follows:

    public class InterfaceSerializer {
    
        @XmlRootElement
        public static class Pojo {
            Pojo() {
                field1 = new PojoFieldImpl1();
                field2 = new PojoFieldImpl2();
                field3 = new PojoFieldImpl1();
            }
    
            @XmlAnyElement
            public IPojoField field1;
            @XmlAnyElement
            public IPojoField field2;
            @XmlAnyElement
            public IPojoField field3;
    
            @Override
            public String toString() {
                return "field1 = " + field1 + "\nfield2 = " + field2 + "\nfield3 = " + field3;
            }
        }
    
        public static interface IPojoField {
    
        }
    
        @XmlRootElement
        public static class PojoFieldImpl1 implements IPojoField {
    
            PojoFieldImpl1() {
                value = "PojoFieldImpl1 value";
            }
    
            public String value;
    
            @Override
            public String toString() {
                return value;
            }
        }
    
        @XmlRootElement
        public static class PojoFieldImpl2  implements IPojoField {
    
            PojoFieldImpl2() {
                value = "PojoFieldImpl2 value1";
                value2 = "PojoFieldImpl2 value2";
            }
    
            public String value;
            public String value2;
    
            @Override
            public String toString() {
                return value + " " + value2;
            }
        }
    
        public static void main(String []args) throws JAXBException {
            Pojo pojo = new Pojo();
            JAXBContext jaxbContext = JAXBContext.newInstance(Pojo.class, PojoFieldImpl1.class, PojoFieldImpl2.class);
            Marshaller marshaller = jaxbContext.createMarshaller();
    
            marshaller.marshal(pojo, new File("interfaceSerializer.xml"));
        }
    }
    

    Output XML:

    
    
        
            PojoFieldImpl1 value
        
        
            PojoFieldImpl2 value1
            PojoFieldImpl2 value2
        
        
            PojoFieldImpl1 value
        
    
    

    The downsides of this method:

    • you can't distinguish from XML each individual field from your pojo (same implementations will be written with the same tag)
    • you don't have any type information to Unmarshall your XML (if you would wish to do so)

    These downsides are fixed in the second solution:

    2.@XmlElement(type = Object.class)

    I've stumbled upon this in mikesir87's blog post. Simply replace the @XmlAnyElement annotations from above with @XmlElement(type = Object.class) You should have something like this in the Pojo class from above:

    @XmlElement(type = Object.class)
    public IPojoField field1;
    @XmlElement(type = Object.class)
    public IPojoField field2;
    @XmlElement(type = Object.class)
    public IPojoField field3;
    

    Re-running our example, the resulting XML:

    
    
        
            PojoFieldImpl1 value
        
        
            PojoFieldImpl2 value1
            PojoFieldImpl2 value2
        
        
            PojoFieldImpl1 value
        
    
    

    This can also be deserialized:

    Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
    Pojo unmarshalledPojo = (Pojo) unmarshaller.unmarshal(new File("interfaceSerializer.xml"));  
    System.out.println(unmarshalledPojo);
    

    Resulting output:

    field1 = PojoFieldImpl1 value
    field2 = PojoFieldImpl2 value1 PojoFieldImpl2 value2
    field3 = PojoFieldImpl1 value
    

    Probably a "hackish" solution, but it gets the job done.

提交回复
热议问题