今天同事找我咨询JAXB的处理问题,为什么在soap里面一直报infinite loop,我和他解释了这种问题产生的原因是因为2个对象互相引用()在工作代码中是一个复杂的A onetomany B, B manytoone A, A onetomany A, A manytoone A的关系)导致xml解析的时候产生了死循环,为了解决这种死循环只要简单的在一端断开这种引用, JAXB提供的相应的annotation来解决这个问题,可是当我们把
@XmlTransient加在字段上面却发现不起作用,还是跑infinite loop exception,上网google了一把,没发现什么问题,后来去jaxb的网站看了,发现是annotation的生效性没有申明在地段上,问题解决后发现
@XmlTransient直接去掉了引用,使用麻烦,从xml转回对象时需要自己加回成员对象,@XmlInverseReference简化了相关操作,推荐使用。
Reference:
https://jaxb.java.net/tutorial/index.html
http://blog.bdoughan.com/2010/07/jpa-entities-to-xml-bidirectional.html
http://blog.bdoughan.com/2013/03/moxys-xmlinversereference-is-now-truly.html
Top-level Elements: XmlRootElement
A class that describes an XML element that is to be a top-level element, i.e., one that can function as an XML document, should be annotated with XmlRootElement. Its optional elements are name and namespace. By default, the class name is used as the name
XmlAccessorType
You can annotate a package or a top level class with XmlAccessorType, setting its value element to one of the enum constants FIELD, PROPERTY, PUBLIC_MEMBER (default) or NONE. If FIELD is set every non static, non transient field will be automatically bound. PROPERTY instructs JAXB to do this for getter and setter pairs. NONE suppresses bind except for explicitly annotated fields or properties. A class without this annotation inherits the XmlAccessorType setting either from its superclass or from the package setting.
FIELD
Every non static, non transient field in a JAXB-bound class will be automatically bound to XML, unless annotated by XmlTransient.
NONE
None of the fields or properties is bound to XML unless they are specifically annotated with some of the JAXB annotations.
PROPERTY
Every getter/setter pair in a JAXB-bound class will be automatically bound to XML, unless annotated by XmlTransient.
PUBLIC_MEMBER
Every public getter/setter pair and every public field will be automatically bound to XML, unless annotated by XmlTransient.
The other annotation to be mentioned in this context is XmlTransient. It suppresses binding for its target which can be an entire class or a field or a method. This is also useful if you have a name clash resulting from a public field, say foo, and methods getFoo and setFoo.
javax.xml.bind.annotation
Annotation Type XmlAccessorType
@Inherited
@Retention(value=RUNTIME)
@Target(value={PACKAGE,TYPE})
public @interface XmlAccessorType
Controls whether fields or Javabean properties are serialized by default.
Usage
@XmlAccessorType annotation can be used with the following program elements:
package
a top level class
See "Package Specification" in javax.xml.bind.package javadoc for additional common information.
This annotation provides control over the default serialization of properties and fields in a class.
The annotation @XmlAccessorType on a package applies to all classes in the package. The following inheritance semantics apply:
If there is a @XmlAccessorType on a class, then it is used.
Otherwise, if a @XmlAccessorType exists on one of its super classes, then it is inherited.
Otherwise, the @XmlAccessorType on a package is inherited.
Defaulting Rules:
By default, if @XmlAccessorType on a package is absent, then the following package level annotation is assumed.
@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
By default, if @XmlAccessorType on a class is absent, and none of its super classes is annotated with @XmlAccessorType, then the following default on the class is assumed:
@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
Example
1.
Address
Here the @XmlInverseReference annotation is used to specify that when the address property is populated on the Customer object, the customer property on the Address object will be set as a back pointer.
Using vanilla JAXB to marshal these objects to XML you need to mark one direction @XmlTrasient, this prevents JAXB from entering an infinite loop during marshalling (object-to-XML). During unmarshalling (XML-to-object) however you are responsible for populating the back pointer yourself.
MOXy offers an extension that will populate the back pointer for you, this is done with the @XmlInverseReference annotation. Note how @XmlInverseReference annotation leverages the same "mappedBy" concept.
Reference:
https://jaxb.java.net/tutorial/index.html
http://blog.bdoughan.com/2010/07/jpa-entities-to-xml-bidirectional.html
http://blog.bdoughan.com/2013/03/moxys-xmlinversereference-is-now-truly.html
Top-level Elements: XmlRootElement
A class that describes an XML element that is to be a top-level element, i.e., one that can function as an XML document, should be annotated with XmlRootElement. Its optional elements are name and namespace. By default, the class name is used as the name
XmlAccessorType
You can annotate a package or a top level class with XmlAccessorType, setting its value element to one of the enum constants FIELD, PROPERTY, PUBLIC_MEMBER (default) or NONE. If FIELD is set every non static, non transient field will be automatically bound. PROPERTY instructs JAXB to do this for getter and setter pairs. NONE suppresses bind except for explicitly annotated fields or properties. A class without this annotation inherits the XmlAccessorType setting either from its superclass or from the package setting.
FIELD
Every non static, non transient field in a JAXB-bound class will be automatically bound to XML, unless annotated by XmlTransient.
NONE
None of the fields or properties is bound to XML unless they are specifically annotated with some of the JAXB annotations.
PROPERTY
Every getter/setter pair in a JAXB-bound class will be automatically bound to XML, unless annotated by XmlTransient.
PUBLIC_MEMBER
Every public getter/setter pair and every public field will be automatically bound to XML, unless annotated by XmlTransient.
The other annotation to be mentioned in this context is XmlTransient. It suppresses binding for its target which can be an entire class or a field or a method. This is also useful if you have a name clash resulting from a public field, say foo, and methods getFoo and setFoo.
javax.xml.bind.annotation
Annotation Type XmlAccessorType
@Inherited
@Retention(value=RUNTIME)
@Target(value={PACKAGE,TYPE})
public @interface XmlAccessorType
Controls whether fields or Javabean properties are serialized by default.
Usage
@XmlAccessorType annotation can be used with the following program elements:
package
a top level class
See "Package Specification" in javax.xml.bind.package javadoc for additional common information.
This annotation provides control over the default serialization of properties and fields in a class.
The annotation @XmlAccessorType on a package applies to all classes in the package. The following inheritance semantics apply:
If there is a @XmlAccessorType on a class, then it is used.
Otherwise, if a @XmlAccessorType exists on one of its super classes, then it is inherited.
Otherwise, the @XmlAccessorType on a package is inherited.
Defaulting Rules:
By default, if @XmlAccessorType on a package is absent, then the following package level annotation is assumed.
@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
By default, if @XmlAccessorType on a class is absent, and none of its super classes is annotated with @XmlAccessorType, then the following default on the class is assumed:
@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
Example
1.
@XmlAccessorType( XmlAccessType.PUBLIC_MEMBER )
public class SomeClass {
private String a;
private String b;
public SomeClass(){ ... }
public String getA(){ ... }
public void setA( String value ){ ... }
@XmlTransient //transient should mentioned on get/set method or public field for public_memeber type
public String getB(){ ... }
public void setB( String value ){ ... }
}
package blog.inversereference;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlInverseReference;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
String name;
@XmlElement
@XmlInverseReference(mappedBy="customer")
//transient/xmlInverseReference should metioned on private field for field type
Address address;
}
Address
Here the @XmlInverseReference annotation is used to specify that when the address property is populated on the Customer object, the customer property on the Address object will be set as a back pointer.
package blog.inversereference;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.*;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Address {
String street;
String city;
@XmlElement
@XmlInverseReference(mappedBy="address")
Customer customer;
}
import javax.persistence.*;
@Entity
public class Customer {
@Id
private long id;
@OneToOne(mappedBy="customer", cascade={CascadeType.ALL})
private Address address;
}
import javax.persistence.*;
@Entity
public class Address implements Serializable {
@Id
private long id;
@MapsId
@OneToOne
@JoinColumn(name="ID")
private Customer customer;
}
Using vanilla JAXB to marshal these objects to XML you need to mark one direction @XmlTrasient, this prevents JAXB from entering an infinite loop during marshalling (object-to-XML). During unmarshalling (XML-to-object) however you are responsible for populating the back pointer yourself.
import javax.persistence.*;
import javax.xml.bind.annotation.*;
@Entity
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Address implements Serializable {
@Id
private long id;
@OneToOne
@JoinColumn(name="ID")
@MapsId
@XmlTransient
private Customer customer;
}
MOXy offers an extension that will populate the back pointer for you, this is done with the @XmlInverseReference annotation. Note how @XmlInverseReference annotation leverages the same "mappedBy" concept.
import javax.persistence.*;
import org.eclipse.persistence.oxm.annotations.*;
@Entity
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Address implements Serializable {
@Id
private long id;
@OneToOne
@JoinColumn(name="ID")
@MapsId
@XmlInverseReference(mappedBy="address")
private Customer customer;
}