All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.codehaus.jackson.map.ser.JsonValueSerializer Maven / Gradle / Ivy

Go to download

Data Mapper package is a high-performance data binding package built on Jackson JSON processor

There is a newer version: 1.9.13
Show newest version
package org.codehaus.jackson.map.ser;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;

import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.*;
import org.codehaus.jackson.map.annotate.JacksonStdImpl;
import org.codehaus.jackson.schema.SchemaAware;
import org.codehaus.jackson.schema.JsonSchema;
import org.codehaus.jackson.type.JavaType;

/**
 * Serializer class that can serialize Object that have a
 * {@link org.codehaus.jackson.annotate.JsonValue} annotation to
 * indicate that serialization should be done by calling the method
 * annotated, and serializing result it returns.
 * 

* Implementation note: we will post-process resulting serializer * (much like what is done with {@link BeanSerializer}) * to figure out actual serializers for final types. This must be * done from {@link #resolve} method, and NOT from constructor; * otherwise we could end up with an infinite loop. */ @JacksonStdImpl public final class JsonValueSerializer extends SerializerBase implements ResolvableSerializer, SchemaAware { protected final Method _accessorMethod; protected JsonSerializer _valueSerializer; protected final BeanProperty _property; /** * This is a flag that is set in rare (?) cases where this serializer * is used for "natural" types (boolean, int, String, double); and where * we actually must force type information wrapping, even though * one would not normally be added. * * @since 1.7 */ protected boolean _forceTypeInformation; /** * @param ser Explicit serializer to use, if caller knows it (which * occurs if and only if the "value method" was annotated with * {@link org.codehaus.jackson.map.annotate.JsonSerialize#using}), otherwise * null */ public JsonValueSerializer(Method valueMethod, JsonSerializer ser, BeanProperty property) { super(Object.class); _accessorMethod = valueMethod; _valueSerializer = ser; _property = property; } @Override public void serialize(Object bean, JsonGenerator jgen, SerializerProvider prov) throws IOException, JsonGenerationException { try { Object value = _accessorMethod.invoke(bean); if (value == null) { prov.defaultSerializeNull(jgen); return; } JsonSerializer ser = _valueSerializer; if (ser == null) { Class c = value.getClass(); /* 10-Mar-2010, tatu: Ideally we would actually separate out type * serializer from value serializer; but, alas, there's no access * to serializer factory at this point... */ // let's cache it, may be needed soon again ser = prov.findTypedValueSerializer(c, true, _property); } ser.serialize(value, jgen, prov); } catch (IOException ioe) { throw ioe; } catch (Exception e) { Throwable t = e; // Need to unwrap this specific type, to see infinite recursion... while (t instanceof InvocationTargetException && t.getCause() != null) { t = t.getCause(); } // Errors shouldn't be wrapped (and often can't, as well) if (t instanceof Error) { throw (Error) t; } // let's try to indicate the path best we can... throw JsonMappingException.wrapWithPath(t, bean, _accessorMethod.getName() + "()"); } } @Override public void serializeWithType(Object bean, JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer) throws IOException, JsonProcessingException { // Regardless of other parts, first need to find value to serialize: Object value = null; try { value = _accessorMethod.invoke(bean); // and if we got null, can also just write it directly if (value == null) { provider.defaultSerializeNull(jgen); return; } JsonSerializer ser = _valueSerializer; if (ser != null) { // already got a serializer? fabulous, that be easy... /* 09-Dec-2010, tatu: To work around natural type's refusal to add type info, we do * this (note: type is for the wrapper type, not enclosed value!) */ if (_forceTypeInformation) { typeSer.writeTypePrefixForScalar(bean, jgen); } ser.serializeWithType(value, jgen, provider, typeSer); if (_forceTypeInformation) { typeSer.writeTypeSuffixForScalar(bean, jgen); } return; } // But if not, it gets tad trickier (copied from main serialize() method) Class c = value.getClass(); ser = provider.findTypedValueSerializer(c, true, _property); // note: now we have bundled type serializer, so should NOT call with typed version ser.serialize(value, jgen, provider); } catch (IOException ioe) { throw ioe; } catch (Exception e) { Throwable t = e; // Need to unwrap this specific type, to see infinite recursion... while (t instanceof InvocationTargetException && t.getCause() != null) { t = t.getCause(); } // Errors shouldn't be wrapped (and often can't, as well) if (t instanceof Error) { throw (Error) t; } // let's try to indicate the path best we can... throw JsonMappingException.wrapWithPath(t, bean, _accessorMethod.getName() + "()"); } } @Override public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException { return (_valueSerializer instanceof SchemaAware) ? ((SchemaAware) _valueSerializer).getSchema(provider, null) : JsonSchema.getDefaultSchemaNode(); } /* /******************************************************* /* ResolvableSerializer impl /******************************************************* */ /** * We can try to find the actual serializer for value, if we can * statically figure out what the result type must be. */ public void resolve(SerializerProvider provider) throws JsonMappingException { if (_valueSerializer == null) { /* Note: we can only assign serializer statically if the * declared type is final -- if not, we don't really know * the actual type until we get the instance. */ // 10-Mar-2010, tatu: Except if static typing is to be used if (provider.isEnabled(SerializationConfig.Feature.USE_STATIC_TYPING) || Modifier.isFinal(_accessorMethod.getReturnType().getModifiers())) { JavaType t = provider.constructType(_accessorMethod.getGenericReturnType()); // false -> no need to cache /* 10-Mar-2010, tatu: Ideally we would actually separate out type * serializer from value serializer; but, alas, there's no access * to serializer factory at this point... */ _valueSerializer = provider.findTypedValueSerializer(t, false, _property); /* 09-Dec-2010, tatu: Turns out we must add special handling for * cases where "native" (aka "natural") type is being serialized, * using standard serializer */ _forceTypeInformation = isNaturalTypeWithStdHandling(t, _valueSerializer); } } } protected boolean isNaturalTypeWithStdHandling(JavaType type, JsonSerializer ser) { Class cls = type.getRawClass(); // First: do we have a natural type being handled? if (type.isPrimitive()) { if (cls != Integer.TYPE && cls != Boolean.TYPE && cls != Double.TYPE) { return false; } } else { if (cls != String.class && cls != Integer.class && cls != Boolean.class && cls != Double.class) { return false; } } // Second: and it's handled with standard serializer? return (ser.getClass().getAnnotation(JacksonStdImpl.class)) != null; } /* /********************************************************** /* Other methods /********************************************************** */ @Override public String toString() { return "(@JsonValue serializer for method " + _accessorMethod.getDeclaringClass() + "#" + _accessorMethod.getName() + ")"; } }