Suppose you want write a jaxb class in groovy. Why? Because you do not have to write these all getters, setters and other methods. You only have to write your fields down.
@XmlRootElement @HashCodeAndEquals @ToString class Person { String firstName String lastName Integer age }
Lets check if we could unmarshal xml to Person class:
def 'should unmarshall person xml to object'(){ given: JAXBContext jc = JAXBContext.newInstance(Person) String xml = 'JohnSmith20' expect: jc.createUnmarshaller().unmarshal(new StringReader(xml)) == new Person(firstName: 'John', lastName: 'Smith', age: 20) }
When we try this, then we obtain an eception:
com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions groovy.lang.MetaClass is an interface, and JAXB can't handle interfaces. this problem is related to the following location: at groovy.lang.MetaClass at public groovy.lang.MetaClass com.blogspot.przybyszd.jaxbingroovy.Person.getMetaClass() at com.blogspot.przybyszd.jaxbingroovy.Person at com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException$Builder.check(IllegalAnnotationsException.java:91) at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.getTypeInfoSet(JAXBContextImpl.java:445) at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.(JAXBContextImpl.java:277) at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.(JAXBContextImpl.java:124) at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl$JAXBContextBuilder.build(JAXBContextImpl.java:1123) at com.sun.xml.internal.bind.v2.ContextFactory.createContext(ContextFactory.java:147) at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:247) at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:234) at javax.xml.bind.ContextFinder.find(ContextFinder.java:462) at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:641) at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:584) at com.blogspot.przybyszd.jaxbingroovy.PersonJaxbTest.should unmarshall person xml to object(PersonJaxbTest.groovy:10)
It is because groovy defines getMetaClass method for us. Marshaller and Unmarshaller use by default
XmlAccessType.PUBLIC_MEMBER what means that public getters and setters should be used during marshalling/unmarshalling. To solve this just add XmlAccessorType annotatnio with XmlAccessType.FIELD on jaxb class:
@XmlRootElement @EqualsAndHashCode @XmlAccessorType(XmlAccessType.FIELD) class Person { String firstName String lastName Integer age }
Of course if you want to apply this rule for each jaxb class in package, then you could put XmlAccessorType in pacakge-info.java file.
@XmlAccessorType(XmlAccessType.FIELD) package com.blogspot.przybyszd.jaxbingroovy; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType;
Great, it works. Now let’s check out marshaller:
def 'should marshall person'() { given: JAXBContext jc = JAXBContext.newInstance(Person) Person p = new Person(firstName: 'John', lastName: 'Smith', age: 20) StringWriter sw = new StringWriter() when: jc.createMarshaller().marshal(p, sw) then: String xml = sw.toString() GPathResult gPathResult = new XmlSlurper().parseText(xml) gPathResult.name() == 'person' gPathResult.firstName == 'John' gPathResult.lastName == 'Smith' gPathResult.age == '20' }
And it also works. Source is available here.