Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Anyway to pass a constructor parameter to a JAXB Adapter?

I'm using Spring 3 IOC and JAXB/JAX-WS in a WebService that I wrote. I am having a slight issue right now with data that must be rounded prior to returning to the consumer as they are not capable of handling the complete precision of the values.

To minimize the impact on the WS design and calculations, I chose to use an Jaxb XmlAdapter to round the values upon marshalling of my response. Everything works fine.

My issue now is that I would like to make it flexible. Ie: in some cases, I need to round to 2 decimal places, in some 4, etc.. Right now, I have to create a TwoDecimalAdapter and a FourDecimalAdapter and use the appropriate one where necessary in my model definitions. This means code duplication.

Is there anyway to create a generic Rounding Adapter, and pass a parameter to it? For instance, instead of:

@javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter(value=FourDecimalRoundingAdapter.class,type=java.math.BigDecimal.class)

I'd like to be able to do something like:

@javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter(value=new RoundingAdapter(4),type=java.math.BigDecimal.class)

Obviously that doesn't work as JAXB instantiates the adapter itself, but is there any technique I can use to pass parameters to the adapter? I'd love to be able to declare the rounding adapter in Spring and use it that way, but there again, I am unable to devise a reusable solution.

Thanks,

Eric

like image 910
Eric B. Avatar asked Jul 12 '12 14:07

Eric B.


2 Answers

Usage

import com.company.BigDecimalAdapter.X$XXXX;
...

@XmlElement(name = "QTY")
@XmlJavaTypeAdapter(X$XXXX.class)
private BigDecimal quantity;

BigDecimalAdapter

import static java.math.RoundingMode.HALF_UP;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import javax.xml.bind.annotation.adapters.XmlAdapter;

public class BigDecimalAdapter extends XmlAdapter<String, BigDecimal> {

    public static final class X$XX extends BigDecimalAdapter {
        public X$XX() {
            super("#.##");
        }
    }

    public static final class X$00 extends BigDecimalAdapter {
        public X$00() {
            super("#.00");
        }
    }

    public static final class X$XXXX extends BigDecimalAdapter {
        public X$XXXX() {
            super("#.####");
        }
    }

    private final ThreadLocal<DecimalFormat> format;

    public BigDecimalAdapter(String pattern) {
        format = ThreadLocal.withInitial(() -> {
            DecimalFormat df = new DecimalFormat(pattern);
            df.setRoundingMode(HALF_UP);
            return df;
        });
    }

    @Override
    public String marshal(BigDecimal v) throws Exception {
        return format.get().format(v);
    }

    @Override
    public BigDecimal unmarshal(String v) throws Exception {
        return new BigDecimal(v);
    }
}
like image 138
Mike Avatar answered Nov 18 '22 23:11

Mike


I'm not sure how you hook this in with Spring, but below is a description of the JAXB mechanism that you can leverage.

If you have the following:

@javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter(value=RoundingAdapter.class,type=java.math.BigDecimal.class)

Then using the standalone JAXB APIs you could do the following. The code below means whenever the RoundingAdapter is encountered, the specified instance should be used.

marshaller.setAdapter(new RoundingAdapter(4));
unmarshaller.setAdapter(new RoundingAdapter(4));

For More Information

  • http://blog.bdoughan.com/2011/09/mixing-nesting-and-references-with.html
like image 36
bdoughan Avatar answered Nov 18 '22 23:11

bdoughan