How do I request a subset of XMLElements using MOX

2019-08-09 09:36发布

问题:

I have a RESTful service that needs to return only a few of the XmlElements if "selectors" are submitted with the request. The URL will take the form of:

/merchants/{merchantId}/profile?selectors=<field1|field2|....|fieldN>

The selectors are optional, and so far I have implemented the service for the full set of elements to be returned for {merchantId} without selectors specified. Now I'm trying to figure out how to add in this added functionality. I'm sure this is covered in documentation but I can't find where. Any RTFM pointers would be appreciated. Thanks.

回答1:

EclipseLink JAXB (MOXy) does not currently offer a mechanism to selectively indicate which fields/properties are included on a per marshal operation. This sounds like an interesting use case. I would appreciate if you could enter this as an enhancement request using the following link:

  • https://bugs.eclipse.org/bugs/enter_bug.cgi?product=EclipseLink

Below is an example of how you could use a stateful XmlAdapter to implement this use case by exploiting the fact that a JAXB (JSR-222) will not marshal an element when the value is null (see: http://blog.bdoughan.com/2012/04/binding-to-json-xml-handling-null.html).

FieldAdapter

Since we are going to leverage stateful XmlAdapters we're going to need one per field. Since all our XmlAdapters will perform the same logic we can create a super class that the others can extend from.

package forum13094195;

import javax.xml.bind.annotation.adapters.XmlAdapter;

public class FieldAdapter<T> extends XmlAdapter<T, T> {

    private boolean include;

    public FieldAdapter() {
        this.include = true;
    }

    public FieldAdapter(boolean include) {
        this.include = include;
    }

    @Override
    public T marshal(T value) throws Exception {
        if(include) {
            return value;
        }
        return null;
    }

    @Override
    public T unmarshal(T value) throws Exception {
        return value;
    }

}

Field1Adapter

package forum13094195;

public class Field1Adapter extends FieldAdapter<String> {
    public Field1Adapter() {}

    public Field1Adapter(boolean include) {
        super(include);
    }
}

Field2Adapter

package forum13094195;

public class Field2Adapter extends FieldAdapter<Integer>{
    public Field2Adapter() {}

    public Field2Adapter(boolean include) {
        super(include);
    }
}

Field3Adapter

package forum13094195;

public class Field3Adapter extends FieldAdapter<String> {
    public Field3Adapter() {}

    public Field3Adapter(boolean include) {
        super(include);
    }
}

Merchant

The @XmlJavaTypeAdapter annotation is used to specify an XmlAdapter on a field/property.

package forum13094195;

import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

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

    @XmlJavaTypeAdapter(Field1Adapter.class)
    String field1;

    @XmlJavaTypeAdapter(Field2Adapter.class)
    int field2;

    @XmlJavaTypeAdapter(Field3Adapter.class)
    String field3;

}

Demo

The demo code below demonstrates how to set a stateful XmlAdapter on the Marshaller.

package forum13094195;

import javax.xml.bind.*;

public class Demo {

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

        Merchant merchant = new Merchant();
        merchant.field1 = "A";
        merchant.field2 = 2;
        merchant.field3 = "C";

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

        marshaller.setAdapter(new Field1Adapter(false));
        marshaller.setAdapter(new Field2Adapter(false));
        marshaller.setAdapter(new Field3Adapter(true));
        marshaller.marshal(merchant, System.out);
    }

}

Output

Below is the output from running the demo code. By default the entire object is marshalled out. The second document marshalled does not contain the fields we excluded.

<?xml version="1.0" encoding="UTF-8"?>
<merchant>
   <field1>A</field1>
   <field2>2</field2>
   <field3>C</field3>
</merchant>
<?xml version="1.0" encoding="UTF-8"?>
<merchant>
   <field3>C</field3>
</merchant>


回答2:

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

In EclipseLink 2.5.0 we released a new feature called Object Graphs that enables you to marshal/unmarshal a subset of mapped fields/properties.

    // Create the Object Graph
    ObjectGraph subset = JAXBHelper.getJAXBContext(jc).createObjectGraph(Merchant.class);
    subset.addAttributeNodes("field1", "field1", "fieldN");

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

For More Information

  • http://blog.bdoughan.com/2013/03/moxys-object-graphs-partial-models-on.html


标签: rest jaxb moxy