1 package com.fasterxml.jackson.databind; 2 3 import java.io.Closeable; 4 import java.io.IOException; 5 import java.util.Collection; 6 7 import com.fasterxml.jackson.core.*; 8 import com.fasterxml.jackson.databind.jsontype.TypeSerializer; 9 import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider; 10 import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap; 11 import com.fasterxml.jackson.databind.ser.impl.TypeWrappedSerializer; 12 13 /** 14 * Writer class similar to {@link ObjectWriter}, except that it can be used 15 * for writing sequences of values, not just a single value. 16 * The main use case is in writing very long sequences, or sequences where 17 * values are incrementally produced; cases where it would be impractical 18 * or at least inconvenient to construct a wrapper container around values 19 * (or where no JSON array is desired around values). 20 *<p> 21 * Differences from {@link ObjectWriter} include: 22 *<ul> 23 * <li>Instances of {@link SequenceWriter} are stateful, and not thread-safe: 24 * if sharing, external synchronization must be used. 25 * <li>Explicit {@link #close} is needed after all values have been written 26 * ({@link ObjectWriter} can auto-close after individual value writes) 27 *</ul> 28 * 29 * @since 2.5 30 */ 31 public class SequenceWriter 32 implements Versioned, java.io.Closeable, java.io.Flushable 33 { 34 /* 35 /********************************************************** 36 /* Configuration 37 /********************************************************** 38 */ 39 40 protected final DefaultSerializerProvider _provider; 41 protected final SerializationConfig _config; 42 protected final JsonGenerator _generator; 43 44 protected final JsonSerializer<Object> _rootSerializer; 45 protected final TypeSerializer _typeSerializer; 46 47 protected final boolean _closeGenerator; 48 protected final boolean _cfgFlush; 49 protected final boolean _cfgCloseCloseable; 50 51 /* 52 /********************************************************** 53 /* State 54 /********************************************************** 55 */ 56 57 /** 58 * If {@link #_rootSerializer} is not defined (no root type 59 * was used for constructing {@link ObjectWriter}), we will 60 * use simple scheme for keeping track of serializers needed. 61 * Assumption is that 62 */ 63 protected PropertySerializerMap _dynamicSerializers; 64 65 /** 66 * State flag for keeping track of need to write matching END_ARRAY, 67 * if a START_ARRAY was written during initialization 68 */ 69 protected boolean _openArray; 70 protected boolean _closed; 71 72 /* 73 /********************************************************** 74 /* Life-cycle 75 /********************************************************** 76 */ 77 SequenceWriter(DefaultSerializerProvider prov, JsonGenerator gen, boolean closeGenerator, ObjectWriter.Prefetch prefetch)78 public SequenceWriter(DefaultSerializerProvider prov, JsonGenerator gen, 79 boolean closeGenerator, ObjectWriter.Prefetch prefetch) 80 throws IOException 81 { 82 _provider = prov; 83 _generator = gen; 84 _closeGenerator = closeGenerator; 85 _rootSerializer = prefetch.getValueSerializer(); 86 _typeSerializer = prefetch.getTypeSerializer(); 87 88 _config = prov.getConfig(); 89 _cfgFlush = _config.isEnabled(SerializationFeature.FLUSH_AFTER_WRITE_VALUE); 90 _cfgCloseCloseable = _config.isEnabled(SerializationFeature.CLOSE_CLOSEABLE); 91 // important: need to cache "root value" serializers, to handle polymorphic 92 // types properly 93 _dynamicSerializers = PropertySerializerMap.emptyForRootValues(); 94 } 95 96 /** 97 * Internal method called by {@link ObjectWriter}: should not be called by code 98 * outside {@code jackson-databind} classes. 99 */ init(boolean wrapInArray)100 public SequenceWriter init(boolean wrapInArray) throws IOException 101 { 102 if (wrapInArray) { 103 _generator.writeStartArray(); 104 _openArray = true; 105 } 106 return this; 107 } 108 109 /* 110 /********************************************************** 111 /* Public API, basic accessors 112 /********************************************************** 113 */ 114 115 /** 116 * Method that will return version information stored in and read from jar 117 * that contains this class. 118 */ 119 @Override version()120 public Version version() { 121 return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION; 122 } 123 124 /* 125 /********************************************************** 126 /* Public API, write operations, related 127 /********************************************************** 128 */ 129 130 /** 131 * Method for writing given value into output, as part of sequence 132 * to write. If root type was specified for {@link ObjectWriter}, 133 * value must be of compatible type (same or subtype). 134 */ write(Object value)135 public SequenceWriter write(Object value) throws IOException 136 { 137 if (value == null) { 138 _provider.serializeValue(_generator, null); 139 return this; 140 } 141 142 if (_cfgCloseCloseable && (value instanceof Closeable)) { 143 return _writeCloseableValue(value); 144 } 145 JsonSerializer<Object> ser = _rootSerializer; 146 if (ser == null) { 147 Class<?> type = value.getClass(); 148 ser = _dynamicSerializers.serializerFor(type); 149 if (ser == null) { 150 ser = _findAndAddDynamic(type); 151 } 152 } 153 _provider.serializeValue(_generator, value, null, ser); 154 if (_cfgFlush) { 155 _generator.flush(); 156 } 157 return this; 158 } 159 160 /** 161 * Method for writing given value into output, as part of sequence 162 * to write; further, full type (often generic, like {@link java.util.Map} 163 * is passed in case a new 164 * {@link JsonSerializer} needs to be fetched to handle type 165 * 166 * If root type was specified for {@link ObjectWriter}, 167 * value must be of compatible type (same or subtype). 168 */ write(Object value, JavaType type)169 public SequenceWriter write(Object value, JavaType type) throws IOException 170 { 171 if (value == null) { 172 _provider.serializeValue(_generator, null); 173 return this; 174 } 175 176 if (_cfgCloseCloseable && (value instanceof Closeable)) { 177 return _writeCloseableValue(value, type); 178 } 179 /* 15-Dec-2014, tatu: I wonder if this could become problematic. It shouldn't 180 * really, since trying to use differently paramterized types in a sequence 181 * is likely to run into other issues. But who knows; if it does become an 182 * issue, may need to implement alternative, JavaType-based map. 183 */ 184 JsonSerializer<Object> ser = _dynamicSerializers.serializerFor(type.getRawClass()); 185 if (ser == null) { 186 ser = _findAndAddDynamic(type); 187 } 188 _provider.serializeValue(_generator, value, type, ser); 189 if (_cfgFlush) { 190 _generator.flush(); 191 } 192 return this; 193 } 194 writeAll(Object[] value)195 public SequenceWriter writeAll(Object[] value) throws IOException 196 { 197 for (int i = 0, len = value.length; i < len; ++i) { 198 write(value[i]); 199 } 200 return this; 201 } 202 203 // NOTE: redundant wrt variant that takes Iterable, but cannot remove or even 204 // deprecate due to backwards-compatibility needs writeAll(C container)205 public <C extends Collection<?>> SequenceWriter writeAll(C container) throws IOException { 206 for (Object value : container) { 207 write(value); 208 } 209 return this; 210 } 211 212 /** 213 * @since 2.7 214 */ writeAll(Iterable<?> iterable)215 public SequenceWriter writeAll(Iterable<?> iterable) throws IOException 216 { 217 for (Object value : iterable) { 218 write(value); 219 } 220 return this; 221 } 222 223 @Override flush()224 public void flush() throws IOException { 225 if (!_closed) { 226 _generator.flush(); 227 } 228 } 229 230 @Override close()231 public void close() throws IOException 232 { 233 if (!_closed) { 234 _closed = true; 235 if (_openArray) { 236 _openArray = false; 237 _generator.writeEndArray(); 238 } 239 if (_closeGenerator) { 240 _generator.close(); 241 } 242 } 243 } 244 245 /* 246 /********************************************************** 247 /* Internal helper methods, serializer lookups 248 /********************************************************** 249 */ 250 _writeCloseableValue(Object value)251 protected SequenceWriter _writeCloseableValue(Object value) throws IOException 252 { 253 Closeable toClose = (Closeable) value; 254 try { 255 JsonSerializer<Object> ser = _rootSerializer; 256 if (ser == null) { 257 Class<?> type = value.getClass(); 258 ser = _dynamicSerializers.serializerFor(type); 259 if (ser == null) { 260 ser = _findAndAddDynamic(type); 261 } 262 } 263 _provider.serializeValue(_generator, value, null, ser); 264 if (_cfgFlush) { 265 _generator.flush(); 266 } 267 Closeable tmpToClose = toClose; 268 toClose = null; 269 tmpToClose.close(); 270 } finally { 271 if (toClose != null) { 272 try { 273 toClose.close(); 274 } catch (IOException ioe) { } 275 } 276 } 277 return this; 278 } 279 _writeCloseableValue(Object value, JavaType type)280 protected SequenceWriter _writeCloseableValue(Object value, JavaType type) throws IOException 281 { 282 Closeable toClose = (Closeable) value; 283 try { 284 // 15-Dec-2014, tatu: As per above, could be problem that we do not pass generic type 285 JsonSerializer<Object> ser = _dynamicSerializers.serializerFor(type.getRawClass()); 286 if (ser == null) { 287 ser = _findAndAddDynamic(type); 288 } 289 _provider.serializeValue(_generator, value, type, ser); 290 if (_cfgFlush) { 291 _generator.flush(); 292 } 293 Closeable tmpToClose = toClose; 294 toClose = null; 295 tmpToClose.close(); 296 } finally { 297 if (toClose != null) { 298 try { 299 toClose.close(); 300 } catch (IOException ioe) { } 301 } 302 } 303 return this; 304 } 305 _findAndAddDynamic(Class<?> type)306 private final JsonSerializer<Object> _findAndAddDynamic(Class<?> type) throws JsonMappingException 307 { 308 PropertySerializerMap.SerializerAndMapResult result; 309 if (_typeSerializer == null) { 310 result = _dynamicSerializers.findAndAddRootValueSerializer(type, _provider); 311 } else { 312 result = _dynamicSerializers.addSerializer(type, 313 new TypeWrappedSerializer(_typeSerializer, _provider.findValueSerializer(type, null))); 314 } 315 _dynamicSerializers = result.map; 316 return result.serializer; 317 } 318 _findAndAddDynamic(JavaType type)319 private final JsonSerializer<Object> _findAndAddDynamic(JavaType type) throws JsonMappingException 320 { 321 PropertySerializerMap.SerializerAndMapResult result; 322 if (_typeSerializer == null) { 323 result = _dynamicSerializers.findAndAddRootValueSerializer(type, _provider); 324 } else { 325 result = _dynamicSerializers.addSerializer(type, 326 new TypeWrappedSerializer(_typeSerializer, _provider.findValueSerializer(type, null))); 327 } 328 _dynamicSerializers = result.map; 329 return result.serializer; 330 } 331 } 332