JAXB marshalling boolean into a complex type

2019-07-07 17:26发布

问题:

I am new in JAXB and I would like to do something i don't know if it's doable. I have a java class to marshall like this :

@XmlAccessorType(XMLAccessType.NONE)
public class MyClass {
  @XmlElement
  private String a = "x";
  @XmlElement
  private String b = "xx";
  @XmlElement
  private boolean c = true;
  ...
}

and want XML output like this :

<?xml ...?>
<MyClass>
    <a>x</a>
    <b>xx</b>
    <c value="true"/>
</MyClass>

One solution i have in mind is to use a boolean wrapper class to make it work, but i would like to avoid this as it takes me away the ability to use boolean primitive true, false.

Can we do that in JAXB?

回答1:

Leverage An XmlAdapter

You could create an XmlAdapter to get the behaviour you are looking for. An XmlAdapter converts a domain object into another type for the purposes of marshalling and unmarshalling.

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

public class BooleanAdapter extends XmlAdapter<BooleanAdapter.AdaptedBoolean, Boolean> {

    public static class AdaptedBoolean {

        @XmlAttribute
        public boolean value;

    }

    @Override
    public Boolean unmarshal(AdaptedBoolean adaptedBoolean) throws Exception {
        return adaptedBoolean.value;
    }

    @Override
    public AdaptedBoolean marshal(Boolean v) throws Exception {
        AdaptedBoolean adaptedBoolean = new AdaptedBoolean();
        adaptedBoolean.value = v;
        return adaptedBoolean;
    }

}

To Use the XmlAdapter

To use the XmlAdapter your mapped field will need to be of type Boolean instead of boolean. You accessor methods can still be boolean if you want.

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

@XmlAccessorType(XmlAccessType.NONE)
public class MyClass {
    @XmlElement
    private String a = "x";
    @XmlElement
    private String b = "xx";

    @XmlElement
    @XmlJavaTypeAdapter(BooleanAdapter.class)
    private Boolean c = true;

    public boolean getC() {
        return c;
    }

    public void setC(boolean c) {
        this.c = c;
    }
}


回答2:

The XML output of your class is:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<myClass>
    <a>x</a>
    <b>xx</b>
    <c>true</c>
</myClass>

If you want the boolean value to result in

<c value="true" />

then you need a wrapper element around the boolean c field. This can be of type Boolean or some other custom type with the boolean c inside of it, and marked with @XmlAttribute.

Example:

class MyBool {
    @XmlAttribute(name="value")
    private boolean c = true;
}

@XmlAccessorType(XmlAccessType.NONE)
class MyClass {
  @XmlElement
  private String a = "x";
  @XmlElement
  private String b = "xx";
  @XmlElement
  private MyBool c = new MyBool();
}

Output:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<myClass>
    <a>x</a>
    <b>xx</b>
    <c value="true"/>
</myClass>

Note:

If you don't want to use a wrapper, you can still annotate boolean c with @XmlAttribute like this:

@XmlAttribute(name="value")
private boolean c = true;

But then it will be rendered as an attribute of the wrapper class which in this case is MyClass, so the output will be:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<myClass value="true">
    <a>x</a>
    <b>xx</b>
</myClass>