I couldn't find one place that provided the answer, so I had to piece bits of information together from different places -- the spring-dwr schema, examples of classes that extend the provided, etc.
For starters, this was the proper way to configure spring.xml:
<dwr:configuration>
<dwr:init>
<dwr:converter id="durationConverter" class="com.foo.DurationConverter"/>
</dwr:init>
<dwr:convert class="com.sun.org.apache.xerces.internal.jaxp.datatype.DurationImpl" type="durationConverter"/>
<dwr:convert class="javax.xml.datatype.Duration" type="durationConverter"/>
...
</dwr:configuration>
I'm not really sure if I needed both the DurationImpl and the Duration convert declarations, but I've seen messages for both of them, so I put them both in.
Here was how I built my converter:
public class DurationConverter extends BeanConverter {
@Override
public Duration convertInbound(Class<?> type, InboundVariable iv, InboundContext ic) throws MarshallException {
String value = iv.getValue();
// If the text is null then the whole bean is null
if (value == null) {
return null;
}
Duration duration = null;
try {
DatatypeFactory df = DatatypeFactory.newInstance();
duration = df.newDuration(Long.valueOf(value));
} catch (DatatypeConfigurationException ex) {
Logger.getLogger(DurationConverter.class.getName()).log(Level.SEVERE, null, ex);
}
return duration;
}
@Override
public OutboundVariable convertOutbound(Object o, OutboundContext oc) throws MarshallException {
Duration duration = (Duration) o;
String varname = oc.getNextVariableName();
Map<String, OutboundVariable> ovs = new TreeMap<>();
OutboundVariable durationOV = getConverterManager().convertOutbound(duration.getSeconds(), oc);
ovs.put("duration", durationOV);
ObjectJsonOutboundVariable oj = new ObjectJsonOutboundVariable();
oj.setChildren(ovs);
return oj;
}
}
Hope this helps somebody else struggling with the same thing.