1

is anybody try to marshal JAXB object with recurisive referency? I have following class:

public class A {

   private Long id;
   private String name;
   private List<A> aList;

}

and I'd like to marshall it to:

<a>
  <a>
    <a>...</a>
  ...
  </a>
...
</a>

I'm using maven plugin to auto generate JAXB class from XSD. Any suggestions?

user1912592
  • 43
  • 1
  • 5

2 Answers2

1

You could do the following:

A

You can use the @XmlElementRef annotation on the aList field so that the element it uses comes from the @XmlRootElement annotation on the A class.

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class A {

    private Long id;

    private String name;

    @XmlElementRef
    private List<A> aList;

}

Demo

Below is some sample code to prove that everything works.

import java.io.File;
import javax.xml.bind.*;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(A.class);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        File xml = new File("src/forum14306965/input.xml");
        A a = (A) unmarshaller.unmarshal(xml);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(a, System.out);
    }

}

input.xml/Output

Below is the input to and output from running the demo code.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<a>
    <id>1</id>
    <name>A</name>
    <a>
        <id>2</id>
        <name>B</name>
        <a>
            <id>3</id>
            <name>C</name>
        </a>
    </a>
</a>

XML Schema

Below is the XML schema that corresponds to this model. You could generate the model from it or start from the Java model.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <xs:element name="a" type="a"/>

  <xs:complexType name="a">
    <xs:sequence>
      <xs:element name="id" type="xs:long" minOccurs="0"/>
      <xs:element name="name" type="xs:string" minOccurs="0"/>
      <xs:element ref="a" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:complexType>

</xs:schema>
bdoughan
  • 147,609
  • 23
  • 300
  • 400
1

Below is how you can support this use case starting from your XML schema.

XML SCHEMA (schema.xsd)

The following XML schema is based on the comment you made to a previous answer (http://stackoverflow.com/a/14317461/383861).

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://www.example.org" 
    xmlns:tns="http://www.example.org"
    elementFormDefault="qualified">

    <xs:element name="b">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="tns:a" maxOccurs="unbounded" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="a">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="id" type="xs:long" />
                <xs:sequence>
                    <xs:element ref="tns:a" minOccurs="0" maxOccurs="10" />
                </xs:sequence>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

</xs:schema>

GENERATED MODEL

The following code was generated using the xjc tool. The get/set methods and comments have been removed to save space.

A

package org.example;

import java.util.List;
import javax.xml.bind.annotation.*;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "id",
    "a"
})
@XmlRootElement(name = "a")
public class A {

    protected long id;
    protected List<A> a;

}

B

package org.example;

import java.util.List;
import javax.xml.bind.annotation.*;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "a"
})
@XmlRootElement(name = "b")
public class B {

    @XmlElement(required = true)
    protected List<A> a;

}

package-info

@javax.xml.bind.annotation.XmlSchema(
        namespace = "http://www.example.org", 
        elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package org.example;

ObjectFactory

package org.example;

import javax.xml.bind.annotation.XmlRegistry;

@XmlRegistry
public class ObjectFactory {

    public ObjectFactory() {
    }

    public B createB() {
        return new B();
    }

    public A createA() {
        return new A();
    }

}

DEMO CODE

import java.io.File;
import javax.xml.bind.*;
import org.example.A;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance("org.example");

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        File xml = new File("src/forum14306965/input.xml");
        A a = (A) unmarshaller.unmarshal(xml);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(a, System.out);
    }

}

INPUT (input.xml)/OUTPUT

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<a xmlns="http://www.example.org">
    <id>1</id>
    <a>
        <id>2</id>
        <a>
            <id>3</id>
        </a>
    </a>
</a>
bdoughan
  • 147,609
  • 23
  • 300
  • 400
  • Thanks once again for your response. In my environment I receive following exception: `[com.sun.istack.internal.SAXException2: A cycle is detected in the object graph. This will cause infinitely deep XML: com.company.tb.ws.jaxb.A@6cb0dffe -> com.company.tb.ws.jaxb.A@6cb0dffe] at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:317) at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:243) at javax.xml.bind.helpers.AbstractMarshallerImpl.marshal(AbstractMarshallerImpl.java:110) at ...` – user1912592 Jan 15 '13 at 12:50
  • Previous exception is associated with: org.springframework.ws.server.endpoint.adapter.method.jaxb.AbstractJaxb2PayloadMethodProcessor$Jaxb2ResultCallback.domResult(AbstractJaxb2PayloadMethodProcessor.java:317) – user1912592 Jan 15 '13 at 12:54
  • @user1912592 - In the code where you are getting that exception you have data like the following a1 -> a2 -> a3 -> a1 (a cycle). Since you have shared references you are going to need to leverage annotations like `@XmlID`/`@XmlIDREF`: http://blog.bdoughan.com/2010/10/jaxb-and-shared-references-xmlid-and.html – bdoughan Jan 15 '13 at 13:09
  • Ok, but how to autogenerate such annotations with maven-jaxb2-plugin? – user1912592 Jan 15 '13 at 13:22
  • 1
    @user1912592 - First you need to determine what the XML should look like. Your current XML schema corresponds to a tree, but your data is a graph. – bdoughan Jan 15 '13 at 13:25
  • @user1912592 you are going to need to introduce keys for the shared references. Perhaps you could do something like: http://stackoverflow.com/questions/7587095/can-jaxb-marshal-by-containment-at-first-then-marshal-by-xmlidref-for-subsequen/7587727#7587727 – bdoughan Jan 15 '13 at 13:43
  • But how to autogenerate it from maven-jaxb2-plugin? – user1912592 Jan 15 '13 at 13:52