package com.fasterxml.jackson.databind.ser.impl; import java.io.IOException; import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.jsontype.TypeSerializer; import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase; import com.fasterxml.jackson.databind.util.NameTransformer; /** * Specialized POJO serializer that differs from * {@link com.fasterxml.jackson.databind.ser.BeanSerializer} * in that instead of producing a JSON Object it will output * a JSON Array, omitting field names, and serializing values in * specified serialization order. * This behavior is usually triggered by using annotation * {@link com.fasterxml.jackson.annotation.JsonFormat} or its * equivalents. *<p> * This serializer can be used for "simple" instances; and will NOT * be used if one of following is true: *<ul> * <li>Unwrapping is used (no way to expand out array in JSON Object) * </li> * <li>Type information ("type id") is to be used: while this could work * for some embedding methods, it would likely cause conflicts. * </li> * <li>Object Identity ("object id") is used: while references would work, * the problem is inclusion of id itself. * </li> *</ul> * Note that it is theoretically possible that last 2 issues could be addressed * (by reserving room in array, for example); and if so, support improved. *<p> * In cases where array-based output is not feasible, this serializer * can instead delegate to the original Object-based serializer; this * is why a reference is retained to the original serializer. * * @since 2.1 */ public class BeanAsArraySerializer extends BeanSerializerBase { /** * Serializer that would produce JSON Object version; used in * cases where array output can not be used. */ protected final BeanSerializerBase _defaultSerializer; /* /********************************************************** /* Life-cycle: constructors /********************************************************** */ public BeanAsArraySerializer(BeanSerializerBase src) { super(src, (ObjectIdWriter) null); _defaultSerializer = src; } protected BeanAsArraySerializer(BeanSerializerBase src, String[] toIgnore) { super(src, toIgnore); _defaultSerializer = src; } /* /********************************************************** /* Life-cycle: factory methods, fluent factories /********************************************************** */ @Override public JsonSerializer<Object> unwrappingSerializer(NameTransformer transformer) { /* If this gets called, we will just need delegate to the default * serializer, to "undo" as-array serialization */ return _defaultSerializer.unwrappingSerializer(transformer); } @Override public boolean isUnwrappingSerializer() { return false; } @Override public BeanSerializerBase withObjectIdWriter(ObjectIdWriter objectIdWriter) { // can't handle Object Ids, for now, so: return _defaultSerializer.withObjectIdWriter(objectIdWriter); } @Override protected BeanAsArraySerializer withIgnorals(String[] toIgnore) { return new BeanAsArraySerializer(this, toIgnore); } @Override protected BeanSerializerBase asArraySerializer() { // already is one, so: return this; } /* /********************************************************** /* JsonSerializer implementation that differs between impls /********************************************************** */ // Re-defined from base class... @Override public void serializeWithType(Object bean, JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer) throws IOException, JsonGenerationException { /* Should not even get here; but let's be nice and re-route * if need be. */ _defaultSerializer.serializeWithType(bean, jgen, provider, typeSer); } /** * Main serialization method that will delegate actual output to * configured * {@link BeanPropertyWriter} instances. */ @Override public final void serialize(Object bean, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException { // [JACKSON-805] if (provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED) && hasSingleElement(provider)) { serializeAsArray(bean, jgen, provider); return; } /* note: it is assumed here that limitations (type id, object id, * any getter, filtering) have already been checked; so code here * is trivial. */ jgen.writeStartArray(); serializeAsArray(bean, jgen, provider); jgen.writeEndArray(); } /* /********************************************************** /* Field serialization methods /********************************************************** */ private boolean hasSingleElement(SerializerProvider provider) { final BeanPropertyWriter[] props; if (_filteredProps != null && provider.getSerializationView() != null) { props = _filteredProps; } else { props = _props; } return props.length == 1; } protected final void serializeAsArray(Object bean, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException { final BeanPropertyWriter[] props; if (_filteredProps != null && provider.getSerializationView() != null) { props = _filteredProps; } else { props = _props; } int i = 0; try { for (final int len = props.length; i < len; ++i) { BeanPropertyWriter prop = props[i]; if (prop == null) { // can have nulls in filtered list; but if so, MUST write placeholders jgen.writeNull(); } else { prop.serializeAsColumn(bean, jgen, provider); } } // NOTE: any getters can not be supported either //if (_anyGetterWriter != null) { // _anyGetterWriter.getAndSerialize(bean, jgen, provider); //} } catch (Exception e) { String name = (i == props.length) ? "[anySetter]" : props[i].getName(); wrapAndThrow(provider, e, bean, name); } catch (StackOverflowError e) { JsonMappingException mapE = new JsonMappingException("Infinite recursion (StackOverflowError)", e); String name = (i == props.length) ? "[anySetter]" : props[i].getName(); mapE.prependPath(new JsonMappingException.Reference(bean, name)); throw mapE; } } /* /********************************************************** /* Standard methods /********************************************************** */ @Override public String toString() { return "BeanAsArraySerializer for "+handledType().getName(); } }