1

I have some jaxb objects modeling a metadata structure with container objects having values, that could be another container object or just a simple object (e.g. string).

@XmlRootElement(name = "value")
public class Value 
{

    protected SimpleType type;
    protected Container container;

    @XmlElement
    public SimpleType getType()
    {
        return type;
    }

    public void setType(SimpleType type)
    {
        this.type = type;
    }

    @XmlInverseReference(mappedBy="value")
    @XmlElement
    public Container getContainer()
    {
        return container;
    }

    public void setContainer(Container container)
    {
        this.container = container;
    }
}

@XmlRootElement(name = "container")
public class Container 
{
    protected Value value;

    @XmlElement
    public Value getValue()
    {
        return value;
    }

    public void setValue(Value value)
    {
        this.value = value;
    }
}

@XmlRootElement(name = "type")
@XmlEnum
public enum SimpleType
{
        @XmlEnumValue("String")STRING,
        @XmlEnumValue("Boolean")BOOLEAN,
....etc.
}

The XML appears fine, but the JSON ends up having duplicate "container" attributes.

        <container>
          <value>
            <container>
              <value>
                <type>String</type>
              </value>
            </container>
          </value>
        </container>

            "container": {
              "value": {
                "container": {
                  "container": {
                    "value": {
                      "type": "STRING"
                    }
                  }
                }
              }
            }

Any idea why this difference?

tanya
  • 508
  • 4
  • 12

2 Answers2

0

That is because of cyclic dependency between Value and Container

UPD. see JAXB Mapping cyclic references to XML

UPD2. see JsonBackReference and JsonManagedReference

Community
  • 1
  • 1
Alex Stybaev
  • 4,623
  • 3
  • 30
  • 44
0

Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.

Since you have MOXy's @XmlInverseReference annotation in your model you may be interested in how this applies to the JSON case.

input.xml

From what I can tell from your question, the XML representation should be the following if you have a bidirectional relationship between Container and Value:

<?xml version="1.0" encoding="UTF-8"?>
<container>
    <value>
       <type>String</type>
    </value>
</container>

Domain Model

Below is how your domain model could be mapped. I have specified @XmlAccessorType(XmlAccessType.FIELD) and removed the accessor methods to make the example shorter.

Container

package forum10706457;

import javax.xml.bind.annotation.*;

@XmlRootElement(name = "container")
@XmlAccessorType(XmlAccessType.FIELD)
public class Container 
{
    protected Value value;

}

Value

package forum10706457;

import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlInverseReference;

@XmlAccessorType(XmlAccessType.FIELD)
public class Value 
{

    protected SimpleType type;

    @XmlInverseReference(mappedBy="value")
    protected Container container;

}

SimpleType

package forum10706457;

import javax.xml.bind.annotation.*;

@XmlEnum
public enum SimpleType
{
        @XmlEnumValue("String")STRING,
        @XmlEnumValue("Boolean")BOOLEAN,
}

jaxb.properties

The @XmlInverseReference annotation is a MOXy extension, so you need to add a file named jaxb.properties in the same package as the domain model with the following entry to specify MOXy as the JAXB provider.

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

Demo

The code below demonstrates how to load the XML document and then marshal the resulting objects to JSON. A check is done to see if the bidirectional relationship was populated.

package forum10706457;

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

public class Demo {

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

        File xml = new File("src/forum10706457/input.xml");
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        Container container = (Container) unmarshaller.unmarshal(xml);

        System.out.println(container == container.value.container);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty("eclipselink.media-type", "application/json");
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(container, System.out);
    }

}

Output

Below is the output from running the demo code. Note how @XmlEnumValue("String")STRING was leveraged to in the JSON representation.

true
{
   "container" : {
      "value" : {
         "type" : "String"
      }
   }
}

For More Information

bdoughan
  • 147,609
  • 23
  • 300
  • 400