CoreXMLSerializers.java
package com.fasterxml.jackson.databind.ext;
import java.io.IOException;
import java.util.Calendar;
import javax.xml.datatype.Duration;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.type.WritableTypeId;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.fasterxml.jackson.databind.ser.Serializers;
import com.fasterxml.jackson.databind.ser.std.CalendarSerializer;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
/**
* Provider for serializers of XML types that are part of full JDK 1.5, but
* that some alleged 1.5 platforms are missing (Android, GAE).
* And for this reason these are added using more dynamic mechanism.
*<p>
* Note: since many of classes defined are abstract, caller must take
* care not to just use straight equivalency check but rather consider
* subclassing as well.
*/
public class CoreXMLSerializers extends Serializers.Base
{
@Override
public JsonSerializer<?> findSerializer(SerializationConfig config,
JavaType type, BeanDescription beanDesc)
{
Class<?> raw = type.getRawClass();
if (Duration.class.isAssignableFrom(raw)) {
return ToStringSerializer.instance;
}
if (QName.class.isAssignableFrom(raw)) {
return QNameSerializer.instance;
}
if (XMLGregorianCalendar.class.isAssignableFrom(raw)) {
return XMLGregorianCalendarSerializer.instance;
}
return null;
}
@SuppressWarnings("serial")
public static class XMLGregorianCalendarSerializer
extends StdSerializer<XMLGregorianCalendar>
implements ContextualSerializer
{
final static XMLGregorianCalendarSerializer instance = new XMLGregorianCalendarSerializer();
final JsonSerializer<Object> _delegate;
public XMLGregorianCalendarSerializer() {
this(CalendarSerializer.instance);
}
@SuppressWarnings("unchecked")
protected XMLGregorianCalendarSerializer(JsonSerializer<?> del) {
super(XMLGregorianCalendar.class);
_delegate = (JsonSerializer<Object>) del;
}
@Override
public JsonSerializer<?> getDelegatee() {
return _delegate;
}
@Override
public boolean isEmpty(SerializerProvider provider, XMLGregorianCalendar value) {
return _delegate.isEmpty(provider, _convert(value));
}
@Override
public void serialize(XMLGregorianCalendar value, JsonGenerator gen, SerializerProvider provider)
throws IOException {
_delegate.serialize(_convert(value), gen, provider);
}
@Override
public void serializeWithType(XMLGregorianCalendar value, JsonGenerator g, SerializerProvider provider,
TypeSerializer typeSer) throws IOException
{
// 16-Aug-2021, tatu: as per [databind#3217] we cannot simply delegate
// as that would produce wrong Type Id altogether. So redefine
// implementation from `StdScalarSerializer`
// _delegate.serializeWithType(_convert(value), gen, provider, typeSer);
// Need not really be string; just indicates "scalar of some kind"
// (and so numeric timestamp is fine as well):
WritableTypeId typeIdDef = typeSer.writeTypePrefix(g,
// important! Pass value AND type to use
typeSer.typeId(value, XMLGregorianCalendar.class, JsonToken.VALUE_STRING));
// note: serialize() will convert to delegate value
serialize(value, g, provider);
typeSer.writeTypeSuffix(g, typeIdDef);
}
@Override
public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException {
_delegate.acceptJsonFormatVisitor(visitor, null);
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
throws JsonMappingException {
JsonSerializer<?> ser = prov.handlePrimaryContextualization(_delegate, property);
if (ser != _delegate) {
return new XMLGregorianCalendarSerializer(ser);
}
return this;
}
protected Calendar _convert(XMLGregorianCalendar input) {
return (input == null) ? null : input.toGregorianCalendar();
}
}
/**
* @since 2.19
*/
public static class QNameSerializer
extends StdSerializer<QName>
implements ContextualSerializer
{
private static final long serialVersionUID = 1L;
public final static JsonSerializer<?> instance = new QNameSerializer();
public QNameSerializer() {
super(QName.class);
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider serializers, BeanProperty property)
throws JsonMappingException
{
JsonFormat.Value format = findFormatOverrides(serializers, property, handledType());
if (format != null) {
JsonFormat.Shape shape = format.getShape();
if (shape == JsonFormat.Shape.OBJECT) {
return this;
}
}
return ToStringSerializer.instance;
}
@Override
public void serialize(QName value, JsonGenerator g, SerializerProvider ctxt)
throws IOException
{
g.writeStartObject(value);
serializeProperties(value, g, ctxt);
g.writeEndObject();
}
@Override
public final void serializeWithType(QName value, JsonGenerator g, SerializerProvider ctxt,
TypeSerializer typeSer)
throws IOException
{
WritableTypeId typeIdDef = typeSer.writeTypePrefix(g,
typeSer.typeId(value, JsonToken.START_OBJECT));
serializeProperties(value, g, ctxt);
typeSer.writeTypeSuffix(g, typeIdDef);
}
private void serializeProperties(QName value, JsonGenerator g, SerializerProvider ctxt)
throws IOException
{
g.writeStringField("localPart", value.getLocalPart());
if (!value.getNamespaceURI().isEmpty()) {
g.writeStringField("namespaceURI", value.getNamespaceURI());
}
if (!value.getPrefix().isEmpty()) {
g.writeStringField("prefix", value.getPrefix());
}
}
@Override
public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
throws JsonMappingException {
/*JsonObjectFormatVisitor v =*/ visitor.expectObjectFormat(typeHint);
// TODO: would need to visit properties too, see `BeanSerializerBase`
}
}
}