blob: d8c36f3cf423f8109e596d02c651650f29a4a2ee [file] [log] [blame]
package org.codehaus.jackson.map;
import java.io.*;
import java.text.DateFormat;
import org.codehaus.jackson.*;
import org.codehaus.jackson.io.SegmentedStringWriter;
import org.codehaus.jackson.map.ser.FilterProvider;
import org.codehaus.jackson.type.JavaType;
import org.codehaus.jackson.type.TypeReference;
import org.codehaus.jackson.util.ByteArrayBuilder;
import org.codehaus.jackson.util.DefaultPrettyPrinter;
import org.codehaus.jackson.util.MinimalPrettyPrinter;
import org.codehaus.jackson.util.VersionUtil;
/**
* Builder object that can be used for per-serialization configuration of
* serialization parameters, such as JSON View and root type to use.
* (and thus fully thread-safe with no external synchronization);
* new instances are constructed for different configurations.
* Instances are initially constructed by {@link ObjectMapper} and can be
* reused in completely thread-safe manner with no explicit synchronization
*
* @author tatu
* @since 1.5
*/
public class ObjectWriter
implements Versioned // since 1.6
{
/**
* We need to keep track of explicit disabling of pretty printing;
* easiest to do by a token value.
*/
protected final static PrettyPrinter NULL_PRETTY_PRINTER = new MinimalPrettyPrinter();
/*
/**********************************************************
/* Immutable configuration from ObjectMapper
/**********************************************************
*/
/**
* General serialization configuration settings
*/
protected final SerializationConfig _config;
protected final SerializerProvider _provider;
protected final SerializerFactory _serializerFactory;
/**
* Factory used for constructing {@link JsonGenerator}s
*/
protected final JsonFactory _jsonFactory;
/*
/**********************************************************
/* Configuration that can be changed during building
/**********************************************************
*/
/**
* Specified root serialization type to use; can be same
* as runtime type, but usually one of its super types
*/
protected final JavaType _rootType;
/**
* To allow for dynamic enabling/disabling of pretty printing,
* pretty printer can be optionally configured for writer
* as well
*/
protected final PrettyPrinter _prettyPrinter;
/**
* When using data format that uses a schema, schema is passed
* to generator.
*
* @since 1.8
*/
protected final FormatSchema _schema;
/*
/**********************************************************
/* Life-cycle, constructors
/**********************************************************
*/
/**
* Constructor used by {@link ObjectMapper} for initial instantiation
*/
protected ObjectWriter(ObjectMapper mapper, SerializationConfig config,
JavaType rootType, PrettyPrinter pp)
{
_config = config;
_provider = mapper._serializerProvider;
_serializerFactory = mapper._serializerFactory;
_jsonFactory = mapper._jsonFactory;
_rootType = rootType;
_prettyPrinter = pp;
_schema = null;
}
/**
* Alternative constructor for initial instantiation.
*
* @since 1.7
*/
protected ObjectWriter(ObjectMapper mapper, SerializationConfig config)
{
_config = config;
_provider = mapper._serializerProvider;
_serializerFactory = mapper._serializerFactory;
_jsonFactory = mapper._jsonFactory;
_rootType = null;
_prettyPrinter = null;
_schema = null;
}
/**
* Alternative constructor for initial instantiation.
*
* @since 1.7
*/
protected ObjectWriter(ObjectMapper mapper, SerializationConfig config,
FormatSchema s)
{
_config = config;
_provider = mapper._serializerProvider;
_serializerFactory = mapper._serializerFactory;
_jsonFactory = mapper._jsonFactory;
_rootType = null;
_prettyPrinter = null;
_schema = s;
}
/**
* Copy constructor used for building variations.
*/
protected ObjectWriter(ObjectWriter base, SerializationConfig config,
JavaType rootType, PrettyPrinter pp, FormatSchema s)
{
_config = config;
_provider = base._provider;
_serializerFactory = base._serializerFactory;
_jsonFactory = base._jsonFactory;
_rootType = rootType;
_prettyPrinter = pp;
_schema = s;
}
/**
* Copy constructor used for building variations.
*
* @since 1.7
*/
protected ObjectWriter(ObjectWriter base, SerializationConfig config)
{
_config = config;
_provider = base._provider;
_serializerFactory = base._serializerFactory;
_jsonFactory = base._jsonFactory;
_schema = base._schema;
_rootType = base._rootType;
_prettyPrinter = base._prettyPrinter;
}
/**
* Method that will return version information stored in and read from jar
* that contains this class.
*
* @since 1.6
*/
@Override
public Version version() {
return VersionUtil.versionFor(getClass());
}
/*
/**********************************************************
/* Life-cycle, fluent factories
/**********************************************************
*/
/**
* Method that will construct a new instance that uses specified
* serialization view for serialization (with null basically disables
* view processing)
*/
public ObjectWriter withView(Class<?> view)
{
if (view == _config.getSerializationView()) return this;
return new ObjectWriter(this, _config.withView(view));
}
/**
* Method that will construct a new instance that uses specific type
* as the root type for serialization, instead of runtime dynamic
* type of the root object itself.
*/
public ObjectWriter withType(JavaType rootType)
{
if (rootType == _rootType) return this;
// type is stored here, no need to make a copy of config
return new ObjectWriter(this, _config, rootType, _prettyPrinter, _schema);
}
/**
* Method that will construct a new instance that uses specific type
* as the root type for serialization, instead of runtime dynamic
* type of the root object itself.
*/
public ObjectWriter withType(Class<?> rootType)
{
return withType(_config.constructType(rootType));
}
/**
* @since 1.7
*/
public ObjectWriter withType(TypeReference<?> rootType)
{
return withType(_config.getTypeFactory().constructType(rootType.getType()));
}
/**
* Method that will construct a new instance that will use specified pretty
* printer (or, if null, will not do any pretty-printing)
*
* @since 1.6
*/
public ObjectWriter withPrettyPrinter(PrettyPrinter pp)
{
if (pp == _prettyPrinter) {
return this;
}
// since null would mean "don't care", need to use placeholder to indicate "disable"
if (pp == null) {
pp = NULL_PRETTY_PRINTER;
}
return new ObjectWriter(this, _config, _rootType, pp, _schema);
}
/**
* Method that will construct a new instance that will use the default
* pretty printer for serialization.
*
* @since 1.6
*/
public ObjectWriter withDefaultPrettyPrinter()
{
return withPrettyPrinter(new DefaultPrettyPrinter());
}
/**
* Method that will construct a new instance that uses specified
* provider for resolving filter instances by id.
*
* @since 1.7
*/
public ObjectWriter withFilters(FilterProvider filterProvider)
{
if (filterProvider == _config.getFilterProvider()) { // no change?
return this;
}
return new ObjectWriter(this, _config.withFilters(filterProvider));
}
/**
* @since 1.8
*/
public ObjectWriter withSchema(FormatSchema schema)
{
if (_schema == schema) {
return this;
}
return new ObjectWriter(this, _config, _rootType, _prettyPrinter, schema);
}
/**
* Fluent factory method that will construct a new writer instance that will
* use specified date format for serializing dates; or if null passed, one
* that will serialize dates as numeric timestamps.
*
* @since 1.9
*/
public ObjectWriter withDateFormat(DateFormat df)
{
SerializationConfig newConfig = _config.withDateFormat(df);
if (newConfig == _config) {
return this;
}
return new ObjectWriter(this, newConfig);
}
/*
/**********************************************************
/* Serialization methods; ones from ObjectCodec first
/**********************************************************
*/
/**
* Method that can be used to serialize any Java value as
* JSON output, using provided {@link JsonGenerator}.
*/
public void writeValue(JsonGenerator jgen, Object value)
throws IOException, JsonGenerationException, JsonMappingException
{
if (_config.isEnabled(SerializationConfig.Feature.CLOSE_CLOSEABLE) && (value instanceof Closeable)) {
_writeCloseableValue(jgen, value, _config);
} else {
if (_rootType == null) {
_provider.serializeValue(_config, jgen, value, _serializerFactory);
} else {
_provider.serializeValue(_config, jgen, value, _rootType, _serializerFactory);
}
if (_config.isEnabled(SerializationConfig.Feature.FLUSH_AFTER_WRITE_VALUE)) {
jgen.flush();
}
}
}
/*
/**********************************************************
/* Serialization methods, others
/**********************************************************
*/
/**
* Method that can be used to serialize any Java value as
* JSON output, written to File provided.
*/
public void writeValue(File resultFile, Object value)
throws IOException, JsonGenerationException, JsonMappingException
{
_configAndWriteValue(_jsonFactory.createJsonGenerator(resultFile, JsonEncoding.UTF8), value);
}
/**
* Method that can be used to serialize any Java value as
* JSON output, using output stream provided (using encoding
* {@link JsonEncoding#UTF8}).
*<p>
* Note: method does not close the underlying stream explicitly
* here; however, {@link JsonFactory} this mapper uses may choose
* to close the stream depending on its settings (by default,
* it will try to close it when {@link JsonGenerator} we construct
* is closed).
*/
public void writeValue(OutputStream out, Object value)
throws IOException, JsonGenerationException, JsonMappingException
{
_configAndWriteValue(_jsonFactory.createJsonGenerator(out, JsonEncoding.UTF8), value);
}
/**
* Method that can be used to serialize any Java value as
* JSON output, using Writer provided.
*<p>
* Note: method does not close the underlying stream explicitly
* here; however, {@link JsonFactory} this mapper uses may choose
* to close the stream depending on its settings (by default,
* it will try to close it when {@link JsonGenerator} we construct
* is closed).
*/
public void writeValue(Writer w, Object value)
throws IOException, JsonGenerationException, JsonMappingException
{
_configAndWriteValue(_jsonFactory.createJsonGenerator(w), value);
}
/**
* Method that can be used to serialize any Java value as
* a String. Functionally equivalent to calling
* {@link #writeValue(Writer,Object)} with {@link java.io.StringWriter}
* and constructing String, but more efficient.
*/
public String writeValueAsString(Object value)
throws IOException, JsonGenerationException, JsonMappingException
{
// alas, we have to pull the recycler directly here...
SegmentedStringWriter sw = new SegmentedStringWriter(_jsonFactory._getBufferRecycler());
_configAndWriteValue(_jsonFactory.createJsonGenerator(sw), value);
return sw.getAndClear();
}
/**
* Method that can be used to serialize any Java value as
* a byte array. Functionally equivalent to calling
* {@link #writeValue(Writer,Object)} with {@link java.io.ByteArrayOutputStream}
* and getting bytes, but more efficient.
* Encoding used will be UTF-8.
*/
public byte[] writeValueAsBytes(Object value)
throws IOException, JsonGenerationException, JsonMappingException
{
ByteArrayBuilder bb = new ByteArrayBuilder(_jsonFactory._getBufferRecycler());
_configAndWriteValue(_jsonFactory.createJsonGenerator(bb, JsonEncoding.UTF8), value);
byte[] result = bb.toByteArray();
bb.release();
return result;
}
/*
/**********************************************************
/* Other public methods
/**********************************************************
*/
public boolean canSerialize(Class<?> type)
{
return _provider.hasSerializerFor(_config, type, _serializerFactory);
}
/*
/**********************************************************
/* Internal methods
/**********************************************************
*/
/**
* Method called to configure the generator as necessary and then
* call write functionality
*/
protected final void _configAndWriteValue(JsonGenerator jgen, Object value)
throws IOException, JsonGenerationException, JsonMappingException
{
if (_prettyPrinter != null) {
PrettyPrinter pp = _prettyPrinter;
jgen.setPrettyPrinter((pp == NULL_PRETTY_PRINTER) ? null : pp);
} else if (_config.isEnabled(SerializationConfig.Feature.INDENT_OUTPUT)) {
jgen.useDefaultPrettyPrinter();
}
// [JACKSON-520]: add support for pass-through schema:
if (_schema != null) {
jgen.setSchema(_schema);
}
// [JACKSON-282]: consider Closeable
if (_config.isEnabled(SerializationConfig.Feature.CLOSE_CLOSEABLE) && (value instanceof Closeable)) {
_configAndWriteCloseable(jgen, value, _config);
return;
}
boolean closed = false;
try {
if (_rootType == null) {
_provider.serializeValue(_config, jgen, value, _serializerFactory);
} else {
_provider.serializeValue(_config, jgen, value, _rootType, _serializerFactory);
}
closed = true;
jgen.close();
} finally {
/* won't try to close twice; also, must catch exception (so it
* will not mask exception that is pending)
*/
if (!closed) {
try {
jgen.close();
} catch (IOException ioe) { }
}
}
}
/**
* Helper method used when value to serialize is {@link Closeable} and its <code>close()</code>
* method is to be called right after serialization has been called
*/
private final void _configAndWriteCloseable(JsonGenerator jgen, Object value, SerializationConfig cfg)
throws IOException, JsonGenerationException, JsonMappingException
{
Closeable toClose = (Closeable) value;
try {
if (_rootType == null) {
_provider.serializeValue(cfg, jgen, value, _serializerFactory);
} else {
_provider.serializeValue(cfg, jgen, value, _rootType, _serializerFactory);
}
// [JACKSON-520]: add support for pass-through schema:
if (_schema != null) {
jgen.setSchema(_schema);
}
JsonGenerator tmpJgen = jgen;
jgen = null;
tmpJgen.close();
Closeable tmpToClose = toClose;
toClose = null;
tmpToClose.close();
} finally {
/* Need to close both generator and value, as long as they haven't yet
* been closed
*/
if (jgen != null) {
try {
jgen.close();
} catch (IOException ioe) { }
}
if (toClose != null) {
try {
toClose.close();
} catch (IOException ioe) { }
}
}
}
/**
* Helper method used when value to serialize is {@link Closeable} and its <code>close()</code>
* method is to be called right after serialization has been called
*/
private final void _writeCloseableValue(JsonGenerator jgen, Object value, SerializationConfig cfg)
throws IOException, JsonGenerationException, JsonMappingException
{
Closeable toClose = (Closeable) value;
try {
if (_rootType == null) {
_provider.serializeValue(cfg, jgen, value, _serializerFactory);
} else {
_provider.serializeValue(cfg, jgen, value, _rootType, _serializerFactory);
}
if (_config.isEnabled(SerializationConfig.Feature.FLUSH_AFTER_WRITE_VALUE)) {
jgen.flush();
}
Closeable tmpToClose = toClose;
toClose = null;
tmpToClose.close();
} finally {
if (toClose != null) {
try {
toClose.close();
} catch (IOException ioe) { }
}
}
}
}