GroovyでJAXB使うときには@XmlAccessorType( XmlAccessType.NONE ) する
JAXBの感触を確かめようとGroovyで弄ってみたら、ちょこっとハマりました。
さらっとバインディング対象のクラスを書いてマーシャリングしてみようって以下のようなコードを書いたんですが、
import javax.xml.bind.annotation.* import javax.xml.bind.* @XmlRootElement class Foo { @XmlAttribute String id @XmlAttribute String name } JAXBContext.newInstance(Foo.class).createMarshaller().with { setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshal(new Foo(id:'100', name:'foofoo'), System.out) }
エラーになります。
com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 4 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 Foo.getMetaClass() at Foo groovy.lang.MetaClass does not have a no-arg default constructor. this problem is related to the following location: at groovy.lang.MetaClass at public groovy.lang.MetaClass Foo.getMetaClass() at Foo Class has two properties of the same name "id" this problem is related to the following location: at public java.lang.String Foo.getId() at Foo this problem is related to the following location: at private java.lang.String Foo.id at Foo Class has two properties of the same name "name" this problem is related to the following location: at public java.lang.String Foo.getName() at Foo this problem is related to the following location: at private java.lang.String Foo.name at Foo
idというプロパティが2つ?nameという名前のプロパティが2つ??MetaClassが悪さしてるの???ややこしそうなエラーでした。
で、ちょっと調べてみると…
http://stackoverflow.com/questions/1161147/how-do-i-get-groovy-and-jaxb-to-play-nice-together
The reason it is not working is that each Groovy Class has a metaClass property on it. JAXB is trying to expose this as a JAXB property which obviously fails. Since you don't declare the metaClass property yourself, it is not possible to annotate it to have JAXB ignore it. Instead you and set the XmlAccessType to NONE.
〜中略〜
Example:
@XmlAccessorType( XmlAccessType.NONE ) @XmlRootElement public class PlayerGroovy { @XmlAttribute String value }
「@XmlAccessorType( XmlAccessType.NONE )」て付けてやればmetaClassをマーシャリングしに行かなくなるから良いよって事でした。
早速やってみると…
import javax.xml.bind.annotation.* import javax.xml.bind.* @XmlAccessorType( XmlAccessType.NONE ) @XmlRootElement class Foo { @XmlAttribute String id @XmlAttribute String name } JAXBContext.newInstance(Foo.class).createMarshaller().with { setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshal(new Foo(id:'100', name:'foofoo'), System.out) }
実行してみると…
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><foo name="foofoo" id="100"/>
動いた!てわけで、@XmlAccessorType( XmlAccessType.NONE )重要です。
この@XmlAccessorTypeの記述、XmlTypeにも必要でした。
import javax.xml.bind.annotation.* import javax.xml.bind.* @XmlRootElement @XmlAccessorType( XmlAccessType.NONE ) class Foo { @XmlAttribute String id @XmlAttribute String name @XmlElementWrapper @XmlElement(name='bar') List<Bar> bars = [] } @XmlType @XmlAccessorType( XmlAccessType.NONE ) class Bar { @XmlAttribute String name } JAXBContext.newInstance(Foo.class).createMarshaller().with { setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshal(new Foo(id:'100', name:'foofoo', bars:[new Bar(name:'barbar1'), new Bar(name:'barbar2')]), System.out) }
こんな感じ。