2
votes

The question title is self explanatory. I could replicate this issue in the example found over here: http://blog.bdoughan.com/2013/03/moxys-object-graphs-inputoutput-partial.html

If you just change

@XmlNamedObjectGraph(
    name="simple",
    attributeNodes={
        @XmlNamedAttributeNode("value"),
    }
)

to

@XmlNamedObjectGraph(
    name="simple",
    attributeNodes={
        @XmlNamedAttributeNode("type"),
    }
)

Then in the marsahalled output the <phoneNumber> element is empty and has no attributes.

I was expecting to see type as an attribute of <phoneNumber> element.

I am using EclipseLink MOXy version 2.5.0

Input:

<?xml version="1.0" encoding="UTF-8"?>
<customer id="123">
   <name>Jane Doe</name>
   <billingAddress>
      <street>1 A Street</street>
      <city>Any Town</city>
      <province>Ontario</province>
      <postalCode>A1B 2C3</postalCode>
   </billingAddress>
   <shippingAddress>
      <street>2 B Road</street>
      <city>Another Place</city>
      <province>Quebec</province>
      <postalCode>X7Y 8Z9</postalCode>
   </shippingAddress>
   <phoneNumbers>
      <phoneNumber type="work">555-1111</phoneNumber>
      <phoneNumber type="home">555-2222</phoneNumber>
   </phoneNumbers>
</customer>

Output:

<?xml version="1.0" encoding="UTF-8"?>
<customer id="123">
   <name>Jane Doe</name>
   <billingAddress>
      <city>Any Town</city>
      <province>Ontario</province>
   </billingAddress>
   <phoneNumbers>
      <phoneNumber/>
      <phoneNumber/>
   </phoneNumbers>
</customer>

Expected Output:

<?xml version="1.0" encoding="UTF-8"?>
<customer id="123">
   <name>Jane Doe</name>
   <billingAddress>
      <city>Any Town</city>
      <province>Ontario</province>
   </billingAddress>
   <phoneNumbers>
      <phoneNumber type="work" />
      <phoneNumber type="home" />
   </phoneNumbers>
</customer>

Code for classes (Copy pasted from the above link):

Customer:

package blog.objectgraphs.metadata;

import java.util.List;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.*;

@XmlNamedObjectGraph(
    name="contact info",
    attributeNodes={
        @XmlNamedAttributeNode("name"),
        @XmlNamedAttributeNode(value="billingAddress", subgraph="location"),
        @XmlNamedAttributeNode(value="phoneNumbers", subgraph="simple")
    },
    subgraphs={
        @XmlNamedSubgraph(
            name="location",
            attributeNodes = { 
                @XmlNamedAttributeNode("city"),
                @XmlNamedAttributeNode("province")
            }
        )
    }
)
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {

    @XmlAttribute
    private int id;

    private String name;

    private Address billingAddress;

    private Address shippingAddress;

    @XmlElementWrapper
    @XmlElement(name="phoneNumber")
    private List<PhoneNumber> phoneNumbers;

}

Address:

package blog.objectgraphs.metadata;

import javax.xml.bind.annotation.*;

@XmlAccessorType(XmlAccessType.FIELD)
public class Address {

    private String street;

    private String city;

    private String province;

    private String postalCode;

}

PhoneNumber:

package blog.objectgraphs.metadata;

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

@XmlNamedObjectGraph(
    name="simple",
    attributeNodes={
        @XmlNamedAttributeNode("value"),
    }
)
@XmlAccessorType(XmlAccessType.FIELD)
public class PhoneNumber {

    @XmlAttribute
    private String type;

    @XmlValue
    private String value;

}

Demo:

package blog.objectgraphs.metadata;

import java.io.File;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.MarshallerProperties;

public class Demo {

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

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        File xml = new File("src/blog/objectgraphs/metadata/input.xml");
        Customer customer = (Customer) unmarshaller.unmarshal(xml);

        // Output XML
        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(customer, System.out);

        // Output XML - Based on Object Graph
        marshaller.setProperty(MarshallerProperties.OBJECT_GRAPH, "contact info");
        marshaller.marshal(customer, System.out);

        // Output JSON - Based on Object Graph
        marshaller.setProperty(MarshallerProperties.MEDIA_TYPE, "application/json");
        marshaller.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, false);
        marshaller.setProperty(MarshallerProperties.JSON_WRAPPER_AS_ARRAY_NAME, true);
        marshaller.marshal(customer, System.out);
    }

}
1

1 Answers

2
votes

The issue you are seeing is due to the following bug in the EclipseLink 2.5.0 release:

This issue was fixed in the EclipseLink 2.5.1 release which can be obtained from the link below:

With EclipseLink 2.5.1 you get the following output:

<?xml version="1.0" encoding="UTF-8"?>
<customer>
   <name>Jane Doe</name>
   <billingAddress>
      <city>Any Town</city>
      <province>Ontario</province>
   </billingAddress>
   <phoneNumbers>
      <phoneNumber type="work"/>
      <phoneNumber type="home"/>
   </phoneNumbers>
</customer>

UPDATE

Just 1 difference, earlier it used to marshall phoneNumbers list in JSON as "phoneNumbers" : [ "555-1111", "555-2222" ] but now it marshalls as "phoneNumbers" : [ { "value" : "555-1111" }, { "value" : "555-2222" } ]. The new one is logically more consistent as it represents the true nature of the object graph, however I can think of cases where the old format could be more useful. For example, if you want to send a list of IDs (XMLIDRefs) then sending them as a JSON array of integers looks better than sending a JSON array of objects which in turn contain one integer each

This was a purposeful change that we made. The majority of users preferred having consistent behaviour. The way it is now you can always use the same mechanism to get the value whether or not the filter is applied.

If the only mapped property in a POJO has @XmlValue then the value key isn't used.