org.codehaus.jackson.map.ser.StdSerializers Maven / Gradle / Ivy
package org.codehaus.jackson.map.ser;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Calendar;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.*;
import org.codehaus.jackson.map.type.TypeFactory;
import org.codehaus.jackson.node.ObjectNode;
import org.codehaus.jackson.schema.JsonSerializableSchema;
import org.codehaus.jackson.util.TokenBuffer;
/**
* Container class for serializers used for handling standard JDK-provided
* types
*
* @since 1.5
*/
public class StdSerializers
{
protected StdSerializers() { }
/*
/***********************************************************
/* Abstract base classes
/***********************************************************
*/
/**
* Intermediate base class for limited number of scalar types
* that should never include type information. These are "native"
* types that are default mappings for corresponding JSON scalar
* types: String, Integer, Double and Boolean.
*/
protected abstract static class NonTypedScalarSerializer
extends ScalarSerializerBase
{
protected NonTypedScalarSerializer(Class t) {
super(t);
}
@Override
public final void serializeWithType(T value, JsonGenerator jgen, SerializerProvider provider,
TypeSerializer typeSer)
throws IOException, JsonGenerationException
{
// no type info, just regular serialization
serialize(value, jgen, provider);
}
}
/*
/////////////////////////////////////////////////////////////////
// Concrete serializers, non-numeric primitives, Strings, Classes
/////////////////////////////////////////////////////////////////
*/
/**
* Serializer used for primitive boolean, as well as java.util.Boolean
* wrapper type.
*
* Since this is one of "native" types, no type information is ever
* included on serialization (unlike for most scalar types as of 1.5)
*/
public final static class BooleanSerializer
extends NonTypedScalarSerializer
{
/**
* Whether type serialized is primitive (boolean) or wrapper
* (java.lang.Boolean); if true, former, if false, latter.
*/
final boolean _forPrimitive;
public BooleanSerializer(boolean forPrimitive)
{
super(Boolean.class);
_forPrimitive = forPrimitive;
}
@Override
public void serialize(Boolean value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonGenerationException
{
jgen.writeBoolean(value.booleanValue());
}
@Override
public JsonNode getSchema(SerializerProvider provider, Type typeHint)
throws JsonMappingException
{
/*(ryan) it may not, in fact, be optional, but there's no way
* to tell whether we're referencing a boolean or java.lang.Boolean.
*/
/* 27-Jun-2009, tatu: Now we can tell, after passing
* 'forPrimitive' flag...
*/
return createSchemaNode("boolean", !_forPrimitive);
}
}
/**
* This is the special serializer for regular {@link java.lang.String}s.
*
* Since this is one of "native" types, no type information is ever
* included on serialization (unlike for most scalar types as of 1.5)
*/
public final static class StringSerializer
extends NonTypedScalarSerializer
{
public StringSerializer() { super(String.class); }
@Override
public void serialize(String value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonGenerationException
{
jgen.writeString(value);
}
@Override
public JsonNode getSchema(SerializerProvider provider, Type typeHint)
{
return createSchemaNode("string", true);
}
}
/*
////////////////////////////////////////////////////////////
// Concrete serializers, numerics
////////////////////////////////////////////////////////////
*/
/**
* This is the special serializer for regular {@link java.lang.Integer}s
* (and primitive ints)
*
* Since this is one of "native" types, no type information is ever
* included on serialization (unlike for most scalar types as of 1.5)
*/
public final static class IntegerSerializer
extends NonTypedScalarSerializer
{
public IntegerSerializer() { super(Integer.class); }
@Override
public void serialize(Integer value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonGenerationException
{
jgen.writeNumber(value.intValue());
}
@Override
public JsonNode getSchema(SerializerProvider provider, Type typeHint)
throws JsonMappingException
{
return createSchemaNode("integer", true);
}
}
/**
* Similar to {@link IntegerSerializer}, but will not cast to Integer:
* instead, cast is to {@link java.lang.Number}, and conversion is
* by calling {@link java.lang.Number#intValue}.
*/
public final static class IntLikeSerializer
extends ScalarSerializerBase
{
final static IntLikeSerializer instance = new IntLikeSerializer();
public IntLikeSerializer() { super(Number.class); }
@Override
public void serialize(Number value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonGenerationException
{
jgen.writeNumber(value.intValue());
}
@Override
public JsonNode getSchema(SerializerProvider provider, Type typeHint)
throws JsonMappingException
{
return createSchemaNode("integer", true);
}
}
public final static class LongSerializer
extends ScalarSerializerBase
{
final static LongSerializer instance = new LongSerializer();
public LongSerializer() { super(Long.class); }
@Override
public void serialize(Long value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonGenerationException
{
jgen.writeNumber(value.longValue());
}
@Override
public JsonNode getSchema(SerializerProvider provider, Type typeHint)
{
return createSchemaNode("number", true);
}
}
public final static class FloatSerializer
extends ScalarSerializerBase
{
final static FloatSerializer instance = new FloatSerializer();
public FloatSerializer() { super(Float.class); }
@Override
public void serialize(Float value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonGenerationException
{
jgen.writeNumber(value.floatValue());
}
@Override
public JsonNode getSchema(SerializerProvider provider, Type typeHint)
{
return createSchemaNode("number", true);
}
}
/**
* This is the special serializer for regular {@link java.lang.Double}s
* (and primitive doubles)
*
* Since this is one of "native" types, no type information is ever
* included on serialization (unlike for most scalar types as of 1.5)
*/
public final static class DoubleSerializer
extends NonTypedScalarSerializer
{
final static DoubleSerializer instance = new DoubleSerializer();
public DoubleSerializer() { super(Double.class); }
@Override
public void serialize(Double value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonGenerationException
{
jgen.writeNumber(value.doubleValue());
}
@Override
public JsonNode getSchema(SerializerProvider provider, Type typeHint)
{
return createSchemaNode("number", true);
}
}
/**
* As a fallback, we may need to use this serializer for other
* types of {@link Number}s (custom types).
*/
public final static class NumberSerializer
extends ScalarSerializerBase
{
public final static NumberSerializer instance = new NumberSerializer();
public NumberSerializer() { super(Number.class); }
@Override
public void serialize(Number value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonGenerationException
{
/* These shouldn't match (as there are more specific ones),
* but just to be sure:
*/
if (value instanceof Double) {
jgen.writeNumber(((Double) value).doubleValue());
} else if (value instanceof Float) {
jgen.writeNumber(((Float) value).floatValue());
} else {
// We'll have to use fallback "untyped" number write method
jgen.writeNumber(value.toString());
}
}
@Override
public JsonNode getSchema(SerializerProvider provider, Type typeHint)
{
return createSchemaNode("number", true);
}
}
/*
////////////////////////////////////////////////////////////
// Serializers for JDK date/time data types
////////////////////////////////////////////////////////////
*/
/**
* For time values we should use timestamp, since that is about the only
* thing that can be reliably converted between date-based objects
* and json.
*/
public final static class CalendarSerializer
extends ScalarSerializerBase
{
public final static CalendarSerializer instance = new CalendarSerializer();
public CalendarSerializer() { super(Calendar.class); }
@Override
public void serialize(Calendar value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonGenerationException
{
provider.defaultSerializeDateValue(value.getTimeInMillis(), jgen);
}
@Override
public JsonNode getSchema(SerializerProvider provider, Type typeHint)
{
//TODO: (ryan) add a format for the date in the schema?
return createSchemaNode(provider.isEnabled(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS)
? "number" : "string", true);
}
}
/**
* For efficiency, we will serialize Dates as longs, instead of
* potentially more readable Strings.
*/
public final static class UtilDateSerializer
extends ScalarSerializerBase
{
public final static UtilDateSerializer instance = new UtilDateSerializer();
public UtilDateSerializer() { super(java.util.Date.class); }
@Override
public void serialize(java.util.Date value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonGenerationException
{
provider.defaultSerializeDateValue(value, jgen);
}
@Override
public JsonNode getSchema(SerializerProvider provider, Type typeHint)
throws JsonMappingException
{
//todo: (ryan) add a format for the date in the schema?
return createSchemaNode(provider.isEnabled(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS)
? "number" : "string", true);
}
}
/**
* Compared to regular {@link UtilDateSerializer}, we do use String
* representation here. Why? Basically to truncate of time part, since
* that should not be used by plain SQL date.
*/
public final static class SqlDateSerializer
extends ScalarSerializerBase
{
public SqlDateSerializer() { super(java.sql.Date.class); }
@Override
public void serialize(java.sql.Date value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonGenerationException
{
jgen.writeString(value.toString());
}
@Override
public JsonNode getSchema(SerializerProvider provider, Type typeHint)
{
//todo: (ryan) add a format for the date in the schema?
return createSchemaNode("string", true);
}
}
public final static class SqlTimeSerializer
extends ScalarSerializerBase
{
public SqlTimeSerializer() { super(java.sql.Time.class); }
@Override
public void serialize(java.sql.Time value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonGenerationException
{
jgen.writeString(value.toString());
}
@Override
public JsonNode getSchema(SerializerProvider provider, Type typeHint)
{
return createSchemaNode("string", true);
}
}
/*
////////////////////////////////////////////////////////////
// Other serializers
////////////////////////////////////////////////////////////
*/
/**
* Generic handler for types that implement {@link JsonSerializable}.
*
* Note: given that this is used for anything that implements
* interface, can not be checked for direct class equivalence.
*/
@SuppressWarnings("deprecation")
public final static class SerializableSerializer
extends SerializerBase
{
final static SerializableSerializer instance = new SerializableSerializer();
private SerializableSerializer() { super(JsonSerializable.class); }
@Override
public void serialize(JsonSerializable value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonGenerationException
{
value.serialize(jgen, provider);
}
@Override
public final void serializeWithType(JsonSerializable value, JsonGenerator jgen, SerializerProvider provider,
TypeSerializer typeSer)
throws IOException, JsonGenerationException
{
/* 24-Jan-2009, tatus: This is not quite optimal (perhaps we should
* just create separate serializer...), but works until 2.0 will
* deprecate non-typed interface
*/
if (value instanceof JsonSerializableWithType) {
((JsonSerializableWithType) value).serializeWithType(jgen, provider, typeSer);
} else {
this.serialize(value, jgen, provider);
}
}
@Override
public JsonNode getSchema(SerializerProvider provider, Type typeHint)
throws JsonMappingException
{
ObjectNode objectNode = createObjectNode();
String schemaType = "any";
String objectProperties = null;
String itemDefinition = null;
if (typeHint != null) {
Class> rawClass = TypeFactory.type(typeHint).getRawClass();
if (rawClass.isAnnotationPresent(JsonSerializableSchema.class)) {
JsonSerializableSchema schemaInfo = rawClass.getAnnotation(JsonSerializableSchema.class);
schemaType = schemaInfo.schemaType();
if (!"##irrelevant".equals(schemaInfo.schemaObjectPropertiesDefinition())) {
objectProperties = schemaInfo.schemaObjectPropertiesDefinition();
}
if (!"##irrelevant".equals(schemaInfo.schemaItemDefinition())) {
itemDefinition = schemaInfo.schemaItemDefinition();
}
}
}
objectNode.put("type", schemaType);
if (objectProperties != null) {
try {
objectNode.put("properties", new ObjectMapper().readValue(objectProperties, JsonNode.class));
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
if (itemDefinition != null) {
try {
objectNode.put("items", new ObjectMapper().readValue(itemDefinition, JsonNode.class));
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
objectNode.put("optional", true);
return objectNode;
}
}
/**
* We also want to directly support serialization of {@link TokenBuffer};
* and since it is part of core package, it can not implement
* {@link JsonSerializable} (which is only included in the mapper
* package)
*
* @since 1.5
*/
public final static class TokenBufferSerializer
extends SerializerBase
{
public TokenBufferSerializer() { super(TokenBuffer.class); }
@Override
public void serialize(TokenBuffer value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonGenerationException
{
value.serialize(jgen);
}
/**
* Implementing typed output for contents of a TokenBuffer is very tricky,
* since we do not know for sure what its contents might look like.
* One possibility would be to check the current token, and use that to
* determine if we would output Json Array, Object or scalar value.
* That might or might now work,
* so for now (as of 1.5), let's not output any type information.
*/
@Override
public final void serializeWithType(TokenBuffer value, JsonGenerator jgen, SerializerProvider provider,
TypeSerializer typeSer)
throws IOException, JsonGenerationException
{
serialize(value, jgen, provider);
}
@Override
public JsonNode getSchema(SerializerProvider provider, Type typeHint)
{
/* 01-Jan-2010, tatu: Not 100% sure what we should say here:
* type is basically not known. This seems closest
* approximation
*/
return createSchemaNode("any", true);
}
}
}