JAXB Marshall XML Profile Filter

I have a need to order an object in XML using JAXB.

The Marshall / Unmarshall code works fine, but now we want to filter the resulting XML based on the annotation.

XmlProfile.java

@Retention (RUNTIME)
@Target({TYPE, METHOD, FIELD})
public @interface XmlProfile {
    String[] value();
}

      

FooPojo.java

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

    @XmlElement
    @XmlProfile("config")
    public String bar;

    @XmlElement
    @XmlProfile("operational")
    public String bartender;

    @XmlElement
    @XmlProfile({"config", "operational"})
    public String spy;

}

      

I have read the JAXB documentation but I have not found any examples / threads of how to inject some code into the marshall process other than adapter or eventHandlers.

In particular, I read about event handlers, but I found support for pre-marshal / post-marshal events.

I was expecting to find a beforeFieldMarshal event, but this is not possible, so I decided to write a piece of code that scans the current object and sets null to properties that do not match the current profile:

private class ProfileListener extends Marshaller.Listener {

    private String profile;

    public ProfileListener(String profile) {
        this.profile = profile;
    }

    @Override
    public void beforeMarshal(Object source) {
        if (source == null || profile == null || profile.length() == 0) return;

        Field[] fields = source.getClass().getFields();
        for (Field f : fields){
            XmlProfile xmlProfile = f.getAnnotation(XmlProfile.class);
            if (xmlProfile == null) continue;

            String[] annValues = xmlProfile.value();
            if (annValues == null 
                || annValues.length == 0 
                || findInArray(annValues)) {
                continue;
            }

            // Remove from object
            f.setAccessible(true);
            try {
                f.set(source, null);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        super.beforeMarshal(source); //To change body of generated methods, choose Tools | Templates.
    }

    private boolean findInArray(String[] annValues){
        for (String annVal : annValues) {
            if (profile.equalsIgnoreCase(annVal)){
                return true;
            }
        }
        return false;
    }

}

      

The code works fine, also with nested object, this is the code I get:

Working Pojo

FooPojo fooPojo = new FooPojo();
fooPojo.bar = "Tijuana Cafe";
fooPojo.bartender = "Cynthia";
fooPojo.spy = "Antony Fisco";
fooPojo.phone = "555 555 555";

      

** XML result for configuration profile: **

<fooPojo>
    <bar>Tijuana Cafe</bar>
    <spy>Antony Fisco</spy>
    <phone>555 555 555</phone>
</fooPojo>

      

** XML Result for Work Profile: **

<fooPojo>
    <bartender>Cynthia</bartender>
    <spy>Antony Fisco</spy>
    <phone>555 555 555</phone>
</fooPojo>

      

but has a functional error that I don't know how to solve: the POJO is updated with code by setting null properties.

Is there a better way to do this? I may end up cloning the object and working on it, but I'm not sure if that's the right way.

+3


source to share





All Articles