package com.fasterxml.jackson.databind.ser.impl; import java.io.IOException; import java.util.Set; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.*; 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 { private static final long serialVersionUID = 1L; // since 2.6 /** * 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, Set<String> toIgnore) { super(src, toIgnore); _defaultSerializer = src; } protected BeanAsArraySerializer(BeanSerializerBase src, ObjectIdWriter oiw, Object filterId) { super(src, oiw, filterId); _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 public BeanSerializerBase withFilterId(Object filterId) { return new BeanAsArraySerializer(this, _objectIdWriter, filterId); } @Override protected BeanAsArraySerializer withIgnorals(Set<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, due to differing prefixes @Override public void serializeWithType(Object bean, JsonGenerator gen, SerializerProvider provider, TypeSerializer typeSer) throws IOException { /* 10-Dec-2014, tatu: Not sure if this can be made to work reliably; * but for sure delegating to default implementation will not work. So: */ if (_objectIdWriter != null) { _serializeWithObjectId(bean, gen, provider, typeSer); return; } String typeStr = (_typeId == null) ? null : _customTypeId(bean); if (typeStr == null) { typeSer.writeTypePrefixForArray(bean, gen); } else { typeSer.writeCustomTypePrefixForArray(bean, gen, typeStr); } serializeAsArray(bean, gen, provider); if (typeStr == null) { typeSer.writeTypeSuffixForArray(bean, gen); } else { typeSer.writeCustomTypeSuffixForArray(bean, gen, typeStr); } } /** * Main serialization method that will delegate actual output to * configured * {@link BeanPropertyWriter} instances. */ @Override public final void serialize(Object bean, JsonGenerator gen, SerializerProvider provider) throws IOException { if (provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED) && hasSingleElement(provider)) { serializeAsArray(bean, gen, 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. */ gen.writeStartArray(); // [databind#631]: Assign current value, to be accessible by custom serializers gen.setCurrentValue(bean); serializeAsArray(bean, gen, provider); gen.writeEndArray(); } /* /********************************************************** /* Field serialization methods /********************************************************** */ private boolean hasSingleElement(SerializerProvider provider) { final BeanPropertyWriter[] props; if (_filteredProps != null && provider.getActiveView() != null) { props = _filteredProps; } else { props = _props; } return props.length == 1; } protected final void serializeAsArray(Object bean, JsonGenerator gen, SerializerProvider provider) throws IOException { final BeanPropertyWriter[] props; if (_filteredProps != null && provider.getActiveView() != 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 gen.writeNull(); } else { prop.serializeAsElement(bean, gen, provider); } } // NOTE: any getters can not be supported either //if (_anyGetterWriter != null) { // _anyGetterWriter.getAndSerialize(bean, gen, provider); //} } catch (Exception e) { String name = (i == props.length) ? "[anySetter]" : props[i].getName(); wrapAndThrow(provider, e, bean, name); } catch (StackOverflowError e) { JsonMappingException mapE = JsonMappingException.from(gen, "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(); } }