Resolving circular references in jaxb

I deal with some circular references when implementing the web service level of the project. I use jaxb (latest version, 2.2.7), and even I looked at some tips like here and here. I can't get it to work. This is the basic SSCCE about my problem:

/* * The service interface */ @WebService public interface IMyWS { @WebMethod public List<Class1> cyclicTest(); } /* * Interface implementation */ @WebService(endpointInterface = "com.mycompany.ws.interfaces.IMyWS") public class MyWS implements IMyWS { @XmlRootElement public static class Class1 { @XmlTransient private Class2 class2; public Class1() { } public Class1(Class2 refClass) { class2 = refClass; } public Class2 getClass2() { return class2; } public void setClass2(Class2 class2) { this.class2 = class2; } @Override public String toString() { return this.getClass().getSimpleName(); } } @XmlRootElement public static class Class2 { private Class1 class1; public Class2() { } public Class1 getClass1() { return class1; } public void setClass1(Class1 class1) { this.class1 = class1; } @Override public String toString() { return this.getClass().getSimpleName(); } } @Override public List<Class1> cyclicTest() { //I create an instance of each class, having them a cyclic reference to the other instance Class2 class2 = new Class2(); Class1 class1 = new Class1(class2); class2.setClass1(class1); return Arrays.asList(class1); } } 

And the exception that I encountered when calling cyclicTest() :

 Caused by: javax.xml.bind.MarshalException - with linked exception: [com.sun.istack.SAXException2: Se ha detectado un ciclo en el gráfico de objeto. Esto provocará un XML con profundidad infinita: Class1 -> Class2 -> Class1] at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:326) at com.sun.xml.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:178) at org.apache.cxf.jaxb.JAXBEncoderDecoder.writeObject(JAXBEncoderDecoder.java:537) at org.apache.cxf.jaxb.JAXBEncoderDecoder.marshall(JAXBEncoderDecoder.java:233) ... 50 more Caused by: com.sun.istack.SAXException2: Se ha detectado un ciclo en el gráfico de objeto. Esto provocará un XML con profundidad infinita: Class1 -> Class2 -> Class1 at com.sun.xml.bind.v2.runtime.XMLSerializer.reportError(XMLSerializer.java:249) at com.sun.xml.bind.v2.runtime.XMLSerializer.pushObject(XMLSerializer.java:537) at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:631) at com.sun.xml.bind.v2.runtime.property.SingleElementNodeProperty.serializeBody(SingleElementNodeProperty.java:158) at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:361) at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:696) at com.sun.xml.bind.v2.runtime.property.SingleElementNodeProperty.serializeBody(SingleElementNodeProperty.java:158) at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:361) at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:696) at com.sun.xml.bind.v2.runtime.property.ArrayElementNodeProperty.serializeItem(ArrayElementNodeProperty.java:69) at com.sun.xml.bind.v2.runtime.property.ArrayElementProperty.serializeListBody(ArrayElementProperty.java:172) at com.sun.xml.bind.v2.runtime.property.ArrayERProperty.serializeBody(ArrayERProperty.java:159) at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:361) at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:696) at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl$1.serializeBody(ElementBeanInfoImpl.java:156) at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl$1.serializeBody(ElementBeanInfoImpl.java:131) at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl.serializeBody(ElementBeanInfoImpl.java:333) at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl.serializeRoot(ElementBeanInfoImpl.java:340) at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl.serializeRoot(ElementBeanInfoImpl.java:76) at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.java:494) at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:323) ... 53 more 

I think I have relevant annotations. What am I missing?

+4
source share
3 answers

When you add @XmlTransient to a private property, you must change your XmlAccessType (default to XmlAccessType.PUBLIC_MEMBER ) to XmlAccessType.PROPERTY

PUBLIC_MEMBER is the default access type in JAXB. This means that the JAXB implementation will generate bindings for: open fields, annotated fields, properties

code copied from WA:

 @WebService(endpointInterface = "com.mycompany.ws.interfaces.IMyWS") public class MyWS implements IMyWS { @XmlRootElement @XmlAccessorType(XmlAccessType.PROPERTY) public static class Class1 { private Class2 class2; public Class1() { } public Class1(Class2 refClass) { class2 = refClass; } @XmlTransient public Class2 getClass2() { return class2; } public void setClass2(Class2 class2) { this.class2 = class2; } @Override public String toString() { return this.getClass().getSimpleName(); } } @XmlRootElement public static class Class2 { private Class1 class1; public Class2() { } public Class1 getClass1() { return class1; } public void setClass1(Class1 class1) { this.class1 = class1; } @Override public String toString() { return this.getClass().getSimpleName(); } } public List<Class1> cyclicTest() { //I create an instance of each class, having them a cyclic reference to the other instance Class2 class2 = new Class2(); Class1 class1 = new Class1(class2); class2.setClass1(class1); return Arrays.asList(class1); } public static void main(String[] args) throws JAXBException { JAXBContext ctx = JAXBContext.newInstance(Class1.class); Marshaller m = ctx.createMarshaller(); m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); List<Class1> class1s = new MyWs().cyclicTest(); for (Class1 c1 : class1s){ m.marshal(c1, System.out); } } 
+3
source

Using @XmlID and @XmlIDREF should solve your problem.

Here is what I did:

I added a String field to class 1 for use as id and annotated it with @XmlID . Then I annotated the setClass1() method with @XmlIDREF . My test is in the main method below.

 /* * Interface implementation */ @WebService(endpointInterface = "com.mycompany.ws.interfaces.IMyWS") public class MyWS implements IMyWS { @XmlRootElement public static class Class1 { @XmlID private String id; private Class2 class2; public Class1() { this.id = UUID.randomUUID().toString(); } public Class1(Class2 refClass) { this(); class2 = refClass; } public Class2 getClass2() { return class2; } public void setClass2(Class2 class2) { this.class2 = class2; } @Override public String toString() { return this.getClass().getSimpleName(); } } @XmlRootElement public static class Class2 { private Class1 class1; public Class2() { } public Class1 getClass1() { return class1; } @XmlIDREF public void setClass1(Class1 class1) { this.class1 = class1; } @Override public String toString() { return this.getClass().getSimpleName(); } } @Override public List<Class1> cyclicTest() { //I create an instance of each class, having them a cyclic reference to the other instance Class2 class2 = new Class2(); Class1 class1 = new Class1(class2); class2.setClass1(class1); return Arrays.asList(class1); } public static void main(String[] args) throws JAXBException { JAXBContext ctx = JAXBContext.newInstance(Class1.class); Marshaller m = ctx.createMarshaller(); m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); List<Class1> class1s = new MyWS().cyclicTest(); for (Class1 c1 : class1s){ m.marshal(c1, System.out); } } } 

Exit:

 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <class1> <id>e9e53e73-9a96-4c7c-b919-3ed3d7aa4c5e</id> <class2> <class1>e9e53e73-9a96-4c7c-b919-3ed3d7aa4c5e</class1> </class2> </class1> 
+5
source

Adding W Almir to the answer in order to generate unique identifiers for objects is easier (there is no need to add code to the constructors), annotate the method that returns the identifier. For instance:

 @XmlID public String getId() { return Integer.toString(System.identityHashCode(this)); } 

This seems to work not only for converting cyclic graphs of Java objects to XML, but also for converting XML to cyclic graphs of Java objects.

0
source

Source: https://habr.com/ru/post/1499765/


All Articles