package sh.calaba.org.codehaus.jackson.map; import java.io.Closeable; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.io.Writer; import java.text.DateFormat; import sh.calaba.org.codehaus.jackson.FormatSchema; import sh.calaba.org.codehaus.jackson.JsonEncoding; import sh.calaba.org.codehaus.jackson.JsonFactory; import sh.calaba.org.codehaus.jackson.JsonGenerationException; import sh.calaba.org.codehaus.jackson.JsonGenerator; import sh.calaba.org.codehaus.jackson.PrettyPrinter; import sh.calaba.org.codehaus.jackson.Version; import sh.calaba.org.codehaus.jackson.Versioned; import sh.calaba.org.codehaus.jackson.io.SegmentedStringWriter; import sh.calaba.org.codehaus.jackson.map.ser.FilterProvider; import sh.calaba.org.codehaus.jackson.type.JavaType; import sh.calaba.org.codehaus.jackson.type.TypeReference; import sh.calaba.org.codehaus.jackson.util.ByteArrayBuilder; import sh.calaba.org.codehaus.jackson.util.DefaultPrettyPrinter; import sh.calaba.org.codehaus.jackson.util.MinimalPrettyPrinter; import sh.calaba.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}). *
* 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. *
* 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 close()
* 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 close()
* 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) { }
}
}
}
}