aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/fasterxml/jackson/databind
diff options
context:
space:
mode:
authorTatu Saloranta <tsaloranta@gmail.com>2012-01-22 16:03:47 -0800
committerTatu Saloranta <tsaloranta@gmail.com>2012-01-22 16:03:47 -0800
commite1961745ea5209c66990f76ba94bf8cd02b77387 (patch)
tree5b16fe592c42377c23b6c26eefe0781f99fb28eb /src/main/java/com/fasterxml/jackson/databind
parent1a7c6f905711ecf704576024dfead79aee47b320 (diff)
downloadjackson-databind-e1961745ea5209c66990f76ba94bf8cd02b77387.tar.gz
Implement [JACKSON-764], programmatic way to set root name to use for root-wrapping
Diffstat (limited to 'src/main/java/com/fasterxml/jackson/databind')
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/DeserializationConfig.java63
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java83
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/ObjectReader.java135
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/ObjectWriter.java139
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/SerializationConfig.java37
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java118
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java159
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/ser/StdSerializerProvider.java22
8 files changed, 441 insertions, 315 deletions
diff --git a/src/main/java/com/fasterxml/jackson/databind/DeserializationConfig.java b/src/main/java/com/fasterxml/jackson/databind/DeserializationConfig.java
index 9becbacea..90abbd183 100644
--- a/src/main/java/com/fasterxml/jackson/databind/DeserializationConfig.java
+++ b/src/main/java/com/fasterxml/jackson/databind/DeserializationConfig.java
@@ -37,7 +37,7 @@ import com.fasterxml.jackson.databind.util.LinkedNode;
* with respect to mix-in annotations; where this is guaranteed as
* long as caller follow "copy-then-use" pattern)
*/
-public class DeserializationConfig
+public final class DeserializationConfig
extends MapperConfigBase<DeserializationConfig.Feature, DeserializationConfig>
{
/**
@@ -334,7 +334,15 @@ public class DeserializationConfig
_problemHandlers = problemHandlers;
_nodeFactory = src._nodeFactory;
}
-
+
+ private DeserializationConfig(DeserializationConfig src, String rootName)
+ {
+ super(src, rootName);
+ _deserFeatures = src._deserFeatures;
+ _problemHandlers = src._problemHandlers;
+ _nodeFactory = src._nodeFactory;
+ }
+
/*
/**********************************************************
/* Life-cycle, factory methods from MapperConfig
@@ -365,62 +373,78 @@ public class DeserializationConfig
@Override
public DeserializationConfig withClassIntrospector(ClassIntrospector<? extends BeanDescription> ci) {
- return new DeserializationConfig(this, _base.withClassIntrospector(ci));
+ return _withBase(_base.withClassIntrospector(ci));
}
@Override
public DeserializationConfig withAnnotationIntrospector(AnnotationIntrospector ai) {
- return new DeserializationConfig(this, _base.withAnnotationIntrospector(ai));
+ return _withBase(_base.withAnnotationIntrospector(ai));
}
@Override
public DeserializationConfig withVisibilityChecker(VisibilityChecker<?> vc) {
- return new DeserializationConfig(this, _base.withVisibilityChecker(vc));
+ return _withBase(_base.withVisibilityChecker(vc));
}
@Override
public DeserializationConfig withVisibility(PropertyAccessor forMethod, JsonAutoDetect.Visibility visibility) {
- return new DeserializationConfig(this, _base.withVisibility(forMethod, visibility));
+ return _withBase( _base.withVisibility(forMethod, visibility));
}
@Override
public DeserializationConfig withTypeResolverBuilder(TypeResolverBuilder<?> trb) {
- return new DeserializationConfig(this, _base.withTypeResolverBuilder(trb));
+ return _withBase(_base.withTypeResolverBuilder(trb));
}
@Override
public DeserializationConfig withSubtypeResolver(SubtypeResolver str) {
- return new DeserializationConfig(this, str);
+ return (_subtypeResolver == str) ? this : new DeserializationConfig(this, str);
}
@Override
public DeserializationConfig withPropertyNamingStrategy(PropertyNamingStrategy pns) {
- return new DeserializationConfig(this, _base.withPropertyNamingStrategy(pns));
+ return _withBase(_base.withPropertyNamingStrategy(pns));
+ }
+
+ @Override
+ public DeserializationConfig withRootName(String rootName) {
+ if (rootName == null) {
+ if (_rootName == null) {
+ return this;
+ }
+ } else if (rootName.equals(_rootName)) {
+ return this;
+ }
+ return new DeserializationConfig(this, rootName);
}
@Override
public DeserializationConfig withTypeFactory(TypeFactory tf) {
- return (tf == _base.getTypeFactory()) ? this : new DeserializationConfig(this, _base.withTypeFactory(tf));
+ return _withBase( _base.withTypeFactory(tf));
}
@Override
public DeserializationConfig withDateFormat(DateFormat df) {
- return (df == _base.getDateFormat()) ? this : new DeserializationConfig(this, _base.withDateFormat(df));
+ return _withBase(_base.withDateFormat(df));
}
@Override
public DeserializationConfig withHandlerInstantiator(HandlerInstantiator hi) {
- return (hi == _base.getHandlerInstantiator()) ? this : new DeserializationConfig(this, _base.withHandlerInstantiator(hi));
+ return _withBase(_base.withHandlerInstantiator(hi));
}
@Override
public DeserializationConfig withInsertedAnnotationIntrospector(AnnotationIntrospector ai) {
- return new DeserializationConfig(this, _base.withInsertedAnnotationIntrospector(ai));
+ return _withBase(_base.withInsertedAnnotationIntrospector(ai));
}
@Override
public DeserializationConfig withAppendedAnnotationIntrospector(AnnotationIntrospector ai) {
- return new DeserializationConfig(this, _base.withAppendedAnnotationIntrospector(ai));
+ return _withBase(_base.withAppendedAnnotationIntrospector(ai));
+ }
+
+ private final DeserializationConfig _withBase(BaseSettings newBase) {
+ return (_base == newBase) ? this : new DeserializationConfig(this, newBase);
}
/*
@@ -544,6 +568,15 @@ public class DeserializationConfig
}
return NopAnnotationIntrospector.instance;
}
+
+ @Override
+ public boolean useRootWrapping()
+ {
+ if (_rootName != null) { // empty String disables wrapping; non-empty enables
+ return (_rootName.length() > 0);
+ }
+ return isEnabled(DeserializationConfig.Feature.UNWRAP_ROOT_VALUE);
+ }
/**
* Accessor for getting bean description that only contains class
@@ -582,7 +615,7 @@ public class DeserializationConfig
return vchecker;
}
- public boolean isEnabled(DeserializationConfig.Feature f) {
+ public final boolean isEnabled(DeserializationConfig.Feature f) {
return (_deserFeatures & f.getMask()) != 0;
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java
index 28dec9155..df86884b7 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java
@@ -194,14 +194,12 @@ public class ObjectMapper
protected final static VisibilityChecker<?> STD_VISIBILITY_CHECKER = VisibilityChecker.Std.defaultInstance();
/**
- * This is the default {@link DateFormat} used unless overridden by
- * custom implementation.
+ * Base settings contain defaults used for all {@link ObjectMapper}
+ * instances.
*/
- protected final static DateFormat DEFAULT_DATE_FORMAT = StdDateFormat.instance;
-
protected final static BaseSettings DEFAULT_BASE = new BaseSettings(DEFAULT_INTROSPECTOR,
DEFAULT_ANNOTATION_INTROSPECTOR, STD_VISIBILITY_CHECKER, null, TypeFactory.defaultInstance(),
- null, DEFAULT_DATE_FORMAT, null);
+ null, StdDateFormat.instance, null);
/*
/**********************************************************
@@ -353,7 +351,7 @@ public class ObjectMapper
}
/**
- * Construct mapper that uses specified {@link JsonFactory}
+ * Constructs instance that uses specified {@link JsonFactory}
* for constructing necessary {@link JsonParser}s and/or
* {@link JsonGenerator}s.
*/
@@ -362,25 +360,18 @@ public class ObjectMapper
this(jf, null, null);
}
- public ObjectMapper(JsonFactory jf,
- SerializerProvider sp, DeserializerProvider dp)
- {
- this(jf, sp, dp, null, null);
- }
-
/**
+ * Constructs instance that uses specified {@link JsonFactory}
+ * for constructing necessary {@link JsonParser}s and/or
+ * {@link JsonGenerator}s, and uses given providers for accessing
+ * serializers and deserializers.
*
* @param jf JsonFactory to use: if null, a new {@link MappingJsonFactory} will be constructed
* @param sp SerializerProvider to use: if null, a {@link StdSerializerProvider} will be constructed
* @param dp DeserializerProvider to use: if null, a {@link StdDeserializerProvider} will be constructed
- * @param sconfig Serialization configuration to use; if null, basic {@link SerializationConfig}
- * will be constructed
- * @param dconfig Deserialization configuration to use; if null, basic {@link DeserializationConfig}
- * will be constructed
*/
public ObjectMapper(JsonFactory jf,
- SerializerProvider sp, DeserializerProvider dp,
- SerializationConfig sconfig, DeserializationConfig dconfig)
+ SerializerProvider sp, DeserializerProvider dp)
{
/* 02-Mar-2009, tatu: Important: we MUST default to using
* the mapping factory, otherwise tree serialization will
@@ -399,11 +390,9 @@ public class ObjectMapper
_subtypeResolver = new StdSubtypeResolver();
// and default type factory is shared one
_typeFactory = TypeFactory.defaultInstance();
- _serializationConfig = (sconfig != null) ? sconfig :
- new SerializationConfig(DEFAULT_BASE,
+ _serializationConfig = new SerializationConfig(DEFAULT_BASE,
_subtypeResolver, _mixInAnnotations);
- _deserializationConfig = (dconfig != null) ? dconfig :
- new DeserializationConfig(DEFAULT_BASE,
+ _deserializationConfig = new DeserializationConfig(DEFAULT_BASE,
_subtypeResolver, _mixInAnnotations);
_serializerProvider = (sp == null) ? new StdSerializerProvider.Impl() : sp;
_deserializerProvider = (dp == null) ? new StdDeserializerProvider() : dp;
@@ -601,15 +590,6 @@ public class ObjectMapper
public SerializationConfig getSerializationConfig() {
return _serializationConfig;
}
-
- /**
- * Method for replacing the shared default serialization configuration
- * object.
- */
- public ObjectMapper setSerializationConfig(SerializationConfig cfg) {
- _serializationConfig = cfg;
- return this;
- }
/**
* Method that returns
@@ -622,15 +602,6 @@ public class ObjectMapper
public DeserializationConfig getDeserializationConfig() {
return _deserializationConfig;
}
-
- /**
- * Method for replacing the shared default deserialization configuration
- * object.
- */
- public ObjectMapper setDeserializationConfig(DeserializationConfig cfg) {
- _deserializationConfig = cfg;
- return this;
- }
/*
/**********************************************************
@@ -2660,8 +2631,8 @@ public class ObjectMapper
DeserializationContext ctxt = _createDeserializationContext(jp, cfg);
JsonDeserializer<Object> deser = _findRootDeserializer(cfg, valueType);
// ok, let's get the value
- if (cfg.isEnabled(DeserializationConfig.Feature.UNWRAP_ROOT_VALUE)) {
- result = _unwrapAndDeserialize(jp, valueType, ctxt, deser);
+ if (cfg.useRootWrapping()) {
+ result = _unwrapAndDeserialize(jp, ctxt, cfg, valueType, deser);
} else {
result = deser.deserialize(jp, ctxt);
}
@@ -2686,8 +2657,8 @@ public class ObjectMapper
DeserializationConfig cfg = getDeserializationConfig();
DeserializationContext ctxt = _createDeserializationContext(jp, cfg);
JsonDeserializer<Object> deser = _findRootDeserializer(cfg, valueType);
- if (cfg.isEnabled(DeserializationConfig.Feature.UNWRAP_ROOT_VALUE)) {
- result = _unwrapAndDeserialize(jp, valueType, ctxt, deser);
+ if (cfg.useRootWrapping()) {
+ result = _unwrapAndDeserialize(jp, ctxt, cfg, valueType, deser);
} else {
result = deser.deserialize(jp, ctxt);
}
@@ -2738,32 +2709,36 @@ public class ObjectMapper
return t;
}
- protected Object _unwrapAndDeserialize(JsonParser jp, JavaType rootType,
- DeserializationContext ctxt, JsonDeserializer<Object> deser)
+ protected Object _unwrapAndDeserialize(JsonParser jp, DeserializationContext ctxt,
+ DeserializationConfig config,
+ JavaType rootType, JsonDeserializer<Object> deser)
throws IOException, JsonParseException, JsonMappingException
{
- SerializedString rootName = _deserializerProvider.findExpectedRootName(ctxt.getConfig(), rootType);
+ String expName = config.getRootName();
+ if (expName == null) {
+ SerializedString sstr = _deserializerProvider.findExpectedRootName(config, rootType);
+ expName = sstr.getValue();
+ }
if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
throw JsonMappingException.from(jp, "Current token not START_OBJECT (needed to unwrap root name '"
- +rootName+"'), but "+jp.getCurrentToken());
+ +expName+"'), but "+jp.getCurrentToken());
}
if (jp.nextToken() != JsonToken.FIELD_NAME) {
throw JsonMappingException.from(jp, "Current token not FIELD_NAME (to contain expected root name '"
- +rootName+"'), but "+jp.getCurrentToken());
+ +expName+"'), but "+jp.getCurrentToken());
}
String actualName = jp.getCurrentName();
- if (!rootName.getValue().equals(actualName)) {
- throw JsonMappingException.from(jp, "Root name '"+actualName+"' does not match expected ('"+rootName
- +"') for type "+rootType);
+ if (!expName.equals(actualName)) {
+ throw JsonMappingException.from(jp, "Root name '"+actualName+"' does not match expected ('"
+ +expName+"') for type "+rootType);
}
// ok, then move to value itself....
jp.nextToken();
-
Object result = deser.deserialize(jp, ctxt);
// and last, verify that we now get matching END_OBJECT
if (jp.nextToken() != JsonToken.END_OBJECT) {
throw JsonMappingException.from(jp, "Current token not END_OBJECT (to match wrapper object with root name '"
- +rootName+"'), but "+jp.getCurrentToken());
+ +expName+"'), but "+jp.getCurrentToken());
}
return result;
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java
index ef511ab88..9b5bdd59c 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java
@@ -126,7 +126,7 @@ public class ObjectReader
}
_schema = schema;
_injectableValues = injectableValues;
- _unwrapRoot = config.isEnabled(DeserializationConfig.Feature.UNWRAP_ROOT_VALUE);
+ _unwrapRoot = config.useRootWrapping();
}
/**
@@ -149,7 +149,7 @@ public class ObjectReader
}
_schema = schema;
_injectableValues = injectableValues;
- _unwrapRoot = config.isEnabled(DeserializationConfig.Feature.UNWRAP_ROOT_VALUE);
+ _unwrapRoot = config.useRootWrapping();
}
/**
@@ -167,7 +167,7 @@ public class ObjectReader
_valueToUpdate = base._valueToUpdate;
_schema = base._schema;
_injectableValues = base._injectableValues;
- _unwrapRoot = config.isEnabled(DeserializationConfig.Feature.UNWRAP_ROOT_VALUE);
+ _unwrapRoot = config.useRootWrapping();
}
/**
@@ -226,6 +226,68 @@ public class ObjectReader
DeserializationConfig newConfig = _config.without(first, other);
return (newConfig == _config) ? this : new ObjectReader(this, newConfig);
}
+
+ /**
+ * Method for constructing a new instance with configuration that uses
+ * passed {@link InjectableValues} to provide injectable values.
+ *<p>
+ * Note that the method does NOT change state of this reader, but
+ * rather construct and returns a newly configured instance.
+ */
+ public ObjectReader withInjectableValues(InjectableValues injectableValues)
+ {
+ if (_injectableValues == injectableValues) {
+ return this;
+ }
+ return new ObjectReader(this, _config, _valueType, _valueToUpdate,
+ _schema, injectableValues);
+ }
+
+ /**
+ * Method for constructing a new reader instance with configuration that uses
+ * passed {@link JsonNodeFactory} for constructing {@link JsonNode}
+ * instances.
+ *<p>
+ * Note that the method does NOT change state of this reader, but
+ * rather construct and returns a newly configured instance.
+ */
+ public ObjectReader withNodeFactory(JsonNodeFactory f)
+ {
+ DeserializationConfig newConfig = _config.withNodeFactory(f);
+ return (newConfig == _config) ? this : new ObjectReader(this, newConfig);
+ }
+
+ /**
+ * Method for constructing a new instance with configuration that
+ * specifies what root name to expect for "root name unwrapping".
+ * See {@link DeserializationConfig#withRootName(String)} for
+ * details.
+ *<p>
+ * Note that the method does NOT change state of this reader, but
+ * rather construct and returns a newly configured instance.
+ */
+ public ObjectReader withRootName(String rootName)
+ {
+ DeserializationConfig newConfig = _config.withRootName(rootName);
+ return (newConfig == _config) ? this : new ObjectReader(this, newConfig);
+ }
+
+ /**
+ * Method for constructing a new instance with configuration that
+ * passes specified {@link FormatSchema} to {@link JsonParser} that
+ * is constructed for parsing content.
+ *<p>
+ * Note that the method does NOT change state of this reader, but
+ * rather construct and returns a newly configured instance.
+ */
+ public ObjectReader withSchema(FormatSchema schema)
+ {
+ if (_schema == schema) {
+ return this;
+ }
+ return new ObjectReader(this, _config, _valueType, _valueToUpdate,
+ schema, _injectableValues);
+ }
/**
* Method for constructing a new reader instance that is configured
@@ -276,22 +338,6 @@ public class ObjectReader
{
return withType(_config.getTypeFactory().constructType(valueTypeRef.getType()));
}
-
- /**
- * Method for constructing a new reader instance with configuration that uses
- * passed {@link JsonNodeFactory} for constructing {@link JsonNode}
- * instances.
- *<p>
- * Note that the method does NOT change state of this reader, but
- * rather construct and returns a newly configured instance.
- */
- public ObjectReader withNodeFactory(JsonNodeFactory f)
- {
- // node factory is stored within config, so need to copy that first
- if (f == _config.getNodeFactory()) return this;
- return new ObjectReader(this, _config.withNodeFactory(f), _valueType, _valueToUpdate,
- _schema, _injectableValues);
- }
/**
* Method for constructing a new instance with configuration that
@@ -311,39 +357,6 @@ public class ObjectReader
return new ObjectReader(this, _config, t, value,
_schema, _injectableValues);
}
-
- /**
- * Method for constructing a new instance with configuration that
- * passes specified {@link FormatSchema} to {@link JsonParser} that
- * is constructed for parsing content.
- *<p>
- * Note that the method does NOT change state of this reader, but
- * rather construct and returns a newly configured instance.
- */
- public ObjectReader withSchema(FormatSchema schema)
- {
- if (_schema == schema) {
- return this;
- }
- return new ObjectReader(this, _config, _valueType, _valueToUpdate,
- schema, _injectableValues);
- }
-
- /**
- * Method for constructing a new instance with configuration that uses
- * passed {@link InjectableValues} to provide injectable values.
- *<p>
- * Note that the method does NOT change state of this reader, but
- * rather construct and returns a newly configured instance.
- */
- public ObjectReader withInjectableValues(InjectableValues injectableValues)
- {
- if (_injectableValues == injectableValues) {
- return this;
- }
- return new ObjectReader(this, _config, _valueType, _valueToUpdate,
- _schema, injectableValues);
- }
/*
/**********************************************************
@@ -916,19 +929,23 @@ public class ObjectReader
JavaType rootType, JsonDeserializer<Object> deser)
throws IOException, JsonParseException, JsonMappingException
{
- SerializedString rootName = _provider.findExpectedRootName(ctxt.getConfig(), rootType);
+ String expName = _config.getRootName();
+ if (expName == null) {
+ SerializedString sstr = _provider.findExpectedRootName(_config, rootType);
+ expName = sstr.getValue();
+ }
if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
throw JsonMappingException.from(jp, "Current token not START_OBJECT (needed to unwrap root name '"
- +rootName+"'), but "+jp.getCurrentToken());
+ +expName+"'), but "+jp.getCurrentToken());
}
if (jp.nextToken() != JsonToken.FIELD_NAME) {
throw JsonMappingException.from(jp, "Current token not FIELD_NAME (to contain expected root name '"
- +rootName+"'), but "+jp.getCurrentToken());
+ +expName+"'), but "+jp.getCurrentToken());
}
String actualName = jp.getCurrentName();
- if (!rootName.getValue().equals(actualName)) {
- throw JsonMappingException.from(jp, "Root name '"+actualName+"' does not match expected ('"+rootName
- +"') for type "+rootType);
+ if (!expName.equals(actualName)) {
+ throw JsonMappingException.from(jp, "Root name '"+actualName+"' does not match expected ('"
+ +expName+"') for type "+rootType);
}
// ok, then move to value itself....
jp.nextToken();
@@ -942,7 +959,7 @@ public class ObjectReader
// and last, verify that we now get matching END_OBJECT
if (jp.nextToken() != JsonToken.END_OBJECT) {
throw JsonMappingException.from(jp, "Current token not END_OBJECT (to match wrapper object with root name '"
- +rootName+"'), but "+jp.getCurrentToken());
+ +expName+"'), but "+jp.getCurrentToken());
}
return result;
}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectWriter.java b/src/main/java/com/fasterxml/jackson/databind/ObjectWriter.java
index b7d63fc01..dbe1e4f99 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ObjectWriter.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ObjectWriter.java
@@ -151,8 +151,6 @@ public class ObjectWriter
/**
* Copy constructor used for building variations.
- *
- * @since 1.7
*/
protected ObjectWriter(ObjectWriter base, SerializationConfig config)
{
@@ -223,45 +221,42 @@ public class ObjectWriter
SerializationConfig newConfig = _config.without(first, other);
return (newConfig == _config) ? this : new ObjectWriter(this, newConfig);
}
-
+
/**
- * Method that will construct a new instance that uses specified
- * serialization view for serialization (with null basically disables
- * view processing)
+ * 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.
+ *<p>
+ * Note that the method does NOT change state of this reader, but
+ * rather construct and returns a newly configured instance.
*/
- public ObjectWriter withView(Class<?> view)
+ public ObjectWriter withDateFormat(DateFormat df)
{
- if (view == _config.getSerializationView()) return this;
- return new ObjectWriter(this, _config.withView(view));
- }
-
+ SerializationConfig newConfig = _config.withDateFormat(df);
+ return (newConfig == _config) ? this : new ObjectWriter(this, newConfig);
+ }
+
/**
- * 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.
+ * Method that will construct a new instance that will use the default
+ * pretty printer for serialization.
*/
- public ObjectWriter withType(JavaType rootType)
+ public ObjectWriter withDefaultPrettyPrinter()
{
- 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);
- }
+ return withPrettyPrinter(new DefaultPrettyPrinter());
+ }
/**
- * 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.
+ * Method that will construct a new instance that uses specified
+ * provider for resolving filter instances by id.
*/
- public ObjectWriter withType(Class<?> rootType)
+ public ObjectWriter withFilters(FilterProvider filterProvider)
{
- return withType(_config.constructType(rootType));
+ if (filterProvider == _config.getFilterProvider()) { // no change?
+ return this;
+ }
+ return new ObjectWriter(this, _config.withFilters(filterProvider));
}
- 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)
@@ -279,56 +274,74 @@ public class ObjectWriter
}
/**
- * Method that will construct a new instance that will use the default
- * pretty printer for serialization.
- *
- * @since 1.6
+ * Method for constructing a new instance with configuration that
+ * specifies what root name to use for "root element wrapping".
+ * See {@link SerializationConfig#withRootName(String)} for details.
+ *<p>
+ * Note that method does NOT change state of this reader, but
+ * rather construct and returns a newly configured instance.
*/
- public ObjectWriter withDefaultPrettyPrinter()
+ public ObjectWriter withRootName(String rootName)
{
- return withPrettyPrinter(new DefaultPrettyPrinter());
+ SerializationConfig newConfig = _config.withRootName(rootName);
+ return (newConfig == _config) ? this : new ObjectWriter(this, newConfig);
}
/**
- * Method that will construct a new instance that uses specified
- * provider for resolving filter instances by id.
- *
- * @since 1.7
+ * Method that will construct a new instance that uses specific format schema
+ * for serialization.
+ *<p>
+ * Note that method does NOT change state of this reader, but
+ * rather construct and returns a newly configured instance.
*/
- public ObjectWriter withFilters(FilterProvider filterProvider)
+
+ public ObjectWriter withSchema(FormatSchema schema)
{
- if (filterProvider == _config.getFilterProvider()) { // no change?
- return this;
- }
- return new ObjectWriter(this, _config.withFilters(filterProvider));
+ return (_schema == schema) ? this :
+ new ObjectWriter(this, _config, _rootType, _prettyPrinter, schema);
}
-
+
/**
- * @since 1.8
+ * 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.
+ *<p>
+ * Note that method does NOT change state of this reader, but
+ * rather construct and returns a newly configured instance.
*/
- public ObjectWriter withSchema(FormatSchema schema)
+ public ObjectWriter withType(JavaType rootType)
{
- if (_schema == schema) {
- return this;
- }
- return new ObjectWriter(this, _config, _rootType, _prettyPrinter, schema);
+ return (rootType == _rootType) ? this
+ // type is stored here, no need to make a copy of config
+ : 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));
+ }
+
+ public ObjectWriter withType(TypeReference<?> rootType) {
+ return withType(_config.getTypeFactory().constructType(rootType.getType()));
}
/**
- * 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
+ * Method that will construct a new instance that uses specified
+ * serialization view for serialization (with null basically disables
+ * view processing)
+ *<p>
+ * Note that the method does NOT change state of this reader, but
+ * rather construct and returns a newly configured instance.
*/
- public ObjectWriter withDateFormat(DateFormat df)
+ public ObjectWriter withView(Class<?> view)
{
- SerializationConfig newConfig = _config.withDateFormat(df);
- if (newConfig == _config) {
- return this;
- }
- return new ObjectWriter(this, newConfig);
- }
+ return (view == _config.getSerializationView()) ? this
+ : new ObjectWriter(this, _config.withView(view));
+ }
/*
/**********************************************************
diff --git a/src/main/java/com/fasterxml/jackson/databind/SerializationConfig.java b/src/main/java/com/fasterxml/jackson/databind/SerializationConfig.java
index e3cc63244..3c7c6b0a5 100644
--- a/src/main/java/com/fasterxml/jackson/databind/SerializationConfig.java
+++ b/src/main/java/com/fasterxml/jackson/databind/SerializationConfig.java
@@ -5,10 +5,7 @@ import java.util.*;
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
-import com.fasterxml.jackson.databind.cfg.BaseSettings;
-import com.fasterxml.jackson.databind.cfg.ConfigFeature;
-import com.fasterxml.jackson.databind.cfg.MapperConfig;
-import com.fasterxml.jackson.databind.cfg.MapperConfigBase;
+import com.fasterxml.jackson.databind.cfg.*;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.ClassIntrospector;
import com.fasterxml.jackson.databind.introspect.VisibilityChecker;
@@ -32,7 +29,7 @@ import com.fasterxml.jackson.databind.util.ClassUtil;
* with respect to mix-in annotations; where this is guaranteed as
* long as caller follow "copy-then-use" pattern)
*/
-public class SerializationConfig
+public final class SerializationConfig
extends MapperConfigBase<SerializationConfig.Feature, SerializationConfig>
{
/**
@@ -384,6 +381,15 @@ public class SerializationConfig
_serializationView = src._serializationView;
_filterProvider = src._filterProvider;
}
+
+ private SerializationConfig(SerializationConfig src, String rootName)
+ {
+ super(src, rootName);
+ _serFeatures = src._serFeatures;
+ _serializationInclusion = src._serializationInclusion;
+ _serializationView = src._serializationView;
+ _filterProvider = src._filterProvider;
+ }
/*
/**********************************************************
@@ -465,6 +471,18 @@ public class SerializationConfig
public SerializationConfig withPropertyNamingStrategy(PropertyNamingStrategy pns) {
return _withBase(_base.withPropertyNamingStrategy(pns));
}
+
+ @Override
+ public SerializationConfig withRootName(String rootName) {
+ if (rootName == null) {
+ if (_rootName == null) {
+ return this;
+ }
+ } else if (rootName.equals(_rootName)) {
+ return this;
+ }
+ return new SerializationConfig(this, rootName);
+ }
@Override
public SerializationConfig withTypeFactory(TypeFactory tf) {
@@ -577,6 +595,15 @@ public class SerializationConfig
}
@Override
+ public boolean useRootWrapping()
+ {
+ if (_rootName != null) { // empty String disables wrapping; non-empty enables
+ return (_rootName.length() > 0);
+ }
+ return isEnabled(SerializationConfig.Feature.WRAP_ROOT_VALUE);
+ }
+
+ @Override
public AnnotationIntrospector getAnnotationIntrospector()
{
/* 29-Jul-2009, tatu: it's now possible to disable use of
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java
index 5a34dc037..b5fb47c53 100644
--- a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java
@@ -313,7 +313,6 @@ public abstract class MapperConfig<T extends MapperConfig<T>>
}
return flags;
}
-
/*
/**********************************************************
/* Life-cycle: factory methods
@@ -332,116 +331,6 @@ public abstract class MapperConfig<T extends MapperConfig<T>>
*/
public abstract T without(MapperConfig.Feature... features);
- /**
- * Method for constructing and returning a new instance with different
- * {@link ClassIntrospector}
- * to use.
- *<p>
- * NOTE: make sure to register new instance with <code>ObjectMapper</code>
- * if directly calling this method.
- */
- public abstract T withClassIntrospector(ClassIntrospector<? extends BeanDescription> ci);
-
- /**
- * Method for constructing and returning a new instance with different
- * {@link AnnotationIntrospector} to use (replacing old one).
- *<p>
- * NOTE: make sure to register new instance with <code>ObjectMapper</code>
- * if directly calling this method.
- */
- public abstract T withAnnotationIntrospector(AnnotationIntrospector ai);
-
- /**
- * Method for constructing and returning a new instance with different
- * {@link VisibilityChecker}
- * to use.
- *<p>
- * NOTE: make sure to register new instance with <code>ObjectMapper</code>
- * if directly calling this method.
- */
- public abstract T withVisibilityChecker(VisibilityChecker<?> vc);
-
- /**
- * Method for constructing and returning a new instance with different
- * minimal visibility level for specified property type
- *<p>
- * NOTE: make sure to register new instance with <code>ObjectMapper</code>
- * if directly calling this method.
- */
- public abstract T withVisibility(PropertyAccessor forMethod, JsonAutoDetect.Visibility visibility);
-
- /**
- * Method for constructing and returning a new instance with different
- * {@link TypeResolverBuilder}
- * to use.
- *<p>
- * NOTE: make sure to register new instance with <code>ObjectMapper</code>
- * if directly calling this method.
- */
- public abstract T withTypeResolverBuilder(TypeResolverBuilder<?> trb);
-
- /**
- * Method for constructing and returning a new instance with different
- * {@link SubtypeResolver}
- * to use.
- *<p>
- * NOTE: make sure to register new instance with <code>ObjectMapper</code>
- * if directly calling this method.
- */
- public abstract T withSubtypeResolver(SubtypeResolver str);
-
- /**
- * Method for constructing and returning a new instance with different
- * {@link PropertyNamingStrategy}
- * to use.
- *<p>
- * NOTE: make sure to register new instance with <code>ObjectMapper</code>
- * if directly calling this method.
- */
- public abstract T withPropertyNamingStrategy(PropertyNamingStrategy strategy);
-
- /**
- * Method for constructing and returning a new instance with different
- * {@link TypeFactory}
- * to use.
- *<p>
- * NOTE: make sure to register new instance with <code>ObjectMapper</code>
- * if directly calling this method.
- */
- public abstract T withTypeFactory(TypeFactory typeFactory);
-
- /**
- * Method for constructing and returning a new instance with different
- * {@link DateFormat}
- * to use.
- *<p>
- * NOTE: make sure to register new instance with <code>ObjectMapper</code>
- * if directly calling this method.
- */
- public abstract T withDateFormat(DateFormat df);
-
- /**
- * Method for constructing and returning a new instance with different
- * {@link HandlerInstantiator}
- * to use.
- *<p>
- * NOTE: make sure to register new instance with <code>ObjectMapper</code>
- * if directly calling this method.
- */
- public abstract T withHandlerInstantiator(HandlerInstantiator hi);
-
- /**
- * Method for constructing and returning a new instance with additional
- * {@link AnnotationIntrospector} inserted (as the highest priority one)
- */
- public abstract T withInsertedAnnotationIntrospector(AnnotationIntrospector introspector);
-
- /**
- * Method for constructing and returning a new instance with additional
- * {@link AnnotationIntrospector} appended (as the lowest priority one)
- */
- public abstract T withAppendedAnnotationIntrospector(AnnotationIntrospector introspector);
-
/*
/**********************************************************
/* Configuration: simple features
@@ -488,6 +377,13 @@ public abstract class MapperConfig<T extends MapperConfig<T>>
public final boolean shouldSortPropertiesAlphabetically() {
return isEnabled(MapperConfig.Feature.SORT_PROPERTIES_ALPHABETICALLY);
}
+
+ /**
+ * Accessor for checking whether configuration indicates that
+ * "root wrapping" (use of an extra property/name pair at root level)
+ * is expected or not.
+ */
+ public abstract boolean useRootWrapping();
/*
/**********************************************************
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java
index 2dfafd0d6..79add3375 100644
--- a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java
@@ -1,9 +1,20 @@
package com.fasterxml.jackson.databind.cfg;
+import java.text.DateFormat;
import java.util.Map;
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.AnnotationIntrospector;
+import com.fasterxml.jackson.databind.BeanDescription;
+import com.fasterxml.jackson.databind.HandlerInstantiator;
+import com.fasterxml.jackson.databind.PropertyNamingStrategy;
+import com.fasterxml.jackson.databind.introspect.ClassIntrospector;
+import com.fasterxml.jackson.databind.introspect.VisibilityChecker;
import com.fasterxml.jackson.databind.jsontype.SubtypeResolver;
+import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
import com.fasterxml.jackson.databind.type.ClassKey;
+import com.fasterxml.jackson.databind.type.TypeFactory;
public abstract class MapperConfigBase<CFG extends ConfigFeature,
T extends MapperConfigBase<CFG,T>>
@@ -28,6 +39,13 @@ public abstract class MapperConfigBase<CFG extends ConfigFeature,
* in addition to) ones declared using annotations.
*/
protected final SubtypeResolver _subtypeResolver;
+
+ /**
+ * Explicitly definite root name to use, if any; if empty
+ * String, will disable root-name wrapping; if null, will
+ * use defaults
+ */
+ protected final String _rootName;
/*
/**********************************************************
@@ -45,6 +63,7 @@ public abstract class MapperConfigBase<CFG extends ConfigFeature,
super(base, DEFAULT_MAPPER_FEATURES);
_mixInAnnotations = mixins;
_subtypeResolver = str;
+ _rootName = null;
}
/**
@@ -55,6 +74,15 @@ public abstract class MapperConfigBase<CFG extends ConfigFeature,
super(src);
_mixInAnnotations = src._mixInAnnotations;
_subtypeResolver = src._subtypeResolver;
+ _rootName = src._rootName;
+ }
+
+ protected MapperConfigBase(MapperConfigBase<CFG,T> src, BaseSettings base)
+ {
+ super(base, src._mapperFeatures);
+ _mixInAnnotations = src._mixInAnnotations;
+ _subtypeResolver = src._subtypeResolver;
+ _rootName = src._rootName;
}
protected MapperConfigBase(MapperConfigBase<CFG,T> src, int mapperFeatures)
@@ -62,21 +90,142 @@ public abstract class MapperConfigBase<CFG extends ConfigFeature,
super(src._base, mapperFeatures);
_mixInAnnotations = src._mixInAnnotations;
_subtypeResolver = src._subtypeResolver;
+ _rootName = src._rootName;
}
protected MapperConfigBase(MapperConfigBase<CFG,T> src, SubtypeResolver str) {
super(src);
_mixInAnnotations = src._mixInAnnotations;
_subtypeResolver = str;
+ _rootName = src._rootName;
}
- protected MapperConfigBase(MapperConfigBase<CFG,T> src, BaseSettings base)
- {
- super(base, src._mapperFeatures);
+ protected MapperConfigBase(MapperConfigBase<CFG,T> src, String rootName) {
+ super(src);
_mixInAnnotations = src._mixInAnnotations;
_subtypeResolver = src._subtypeResolver;
+ _rootName = rootName;
}
+
+ /*
+ /**********************************************************
+ /* Addition fluent factory methods, common to all sub-types
+ /**********************************************************
+ */
+
+ /**
+ * Method for constructing and returning a new instance with different
+ * {@link AnnotationIntrospector} to use (replacing old one).
+ *<p>
+ * NOTE: make sure to register new instance with <code>ObjectMapper</code>
+ * if directly calling this method.
+ */
+ public abstract T withAnnotationIntrospector(AnnotationIntrospector ai);
+
+ /**
+ * Method for constructing and returning a new instance with additional
+ * {@link AnnotationIntrospector} appended (as the lowest priority one)
+ */
+ public abstract T withAppendedAnnotationIntrospector(AnnotationIntrospector introspector);
+
+ /**
+ * Method for constructing and returning a new instance with additional
+ * {@link AnnotationIntrospector} inserted (as the highest priority one)
+ */
+ public abstract T withInsertedAnnotationIntrospector(AnnotationIntrospector introspector);
+
+ /**
+ * Method for constructing and returning a new instance with different
+ * {@link ClassIntrospector}
+ * to use.
+ *<p>
+ * NOTE: make sure to register new instance with <code>ObjectMapper</code>
+ * if directly calling this method.
+ */
+ public abstract T withClassIntrospector(ClassIntrospector<? extends BeanDescription> ci);
+
+ /**
+ * Method for constructing and returning a new instance with different
+ * {@link DateFormat}
+ * to use.
+ *<p>
+ * NOTE: make sure to register new instance with <code>ObjectMapper</code>
+ * if directly calling this method.
+ */
+ public abstract T withDateFormat(DateFormat df);
+
+ /**
+ * Method for constructing and returning a new instance with different
+ * {@link HandlerInstantiator}
+ * to use.
+ *<p>
+ * NOTE: make sure to register new instance with <code>ObjectMapper</code>
+ * if directly calling this method.
+ */
+ public abstract T withHandlerInstantiator(HandlerInstantiator hi);
+
+ /**
+ * Method for constructing and returning a new instance with different
+ * {@link PropertyNamingStrategy}
+ * to use.
+ *<p>
+ * NOTE: make sure to register new instance with <code>ObjectMapper</code>
+ * if directly calling this method.
+ */
+ public abstract T withPropertyNamingStrategy(PropertyNamingStrategy strategy);
+
+ /**
+ * Method for constructing and returning a new instance with different
+ * root name to use (none, if null).
+ *<p>
+ * Note that when a root name is set to a non-Empty String, this will automatically force use
+ * of root element wrapping with given name. If empty String passed, will
+ * disable root name wrapping; and if null used, will instead use
+ * <code>Feature</code> to determine if to use wrapping, and annotation
+ * (or default name) for actual root name to use.
+ *
+ * @param rootName to use: if null, means "use default" (clear setting);
+ * if empty String ("") means that no root name wrapping is used;
+ * otherwise defines root name to use.
+ */
+ public abstract T withRootName(String rootName);
+
+ /**
+ * Method for constructing and returning a new instance with different
+ * {@link SubtypeResolver}
+ * to use.
+ *<p>
+ * NOTE: make sure to register new instance with <code>ObjectMapper</code>
+ * if directly calling this method.
+ */
+ public abstract T withSubtypeResolver(SubtypeResolver str);
+
+ /**
+ * Method for constructing and returning a new instance with different
+ * {@link TypeFactory}
+ * to use.
+ */
+ public abstract T withTypeFactory(TypeFactory typeFactory);
+ /**
+ * Method for constructing and returning a new instance with different
+ * {@link TypeResolverBuilder} to use.
+ */
+ public abstract T withTypeResolverBuilder(TypeResolverBuilder<?> trb);
+
+ /**
+ * Method for constructing and returning a new instance with different
+ * {@link VisibilityChecker}
+ * to use.
+ */
+ public abstract T withVisibilityChecker(VisibilityChecker<?> vc);
+
+ /**
+ * Method for constructing and returning a new instance with different
+ * minimal visibility level for specified property type
+ */
+ public abstract T withVisibility(PropertyAccessor forMethod, JsonAutoDetect.Visibility visibility);
+
/*
/**********************************************************
/* Simple accessors
@@ -93,6 +242,10 @@ public abstract class MapperConfigBase<CFG extends ConfigFeature,
public final SubtypeResolver getSubtypeResolver() {
return _subtypeResolver;
}
+
+ public final String getRootName() {
+ return _rootName;
+ }
/*
/**********************************************************
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/StdSerializerProvider.java b/src/main/java/com/fasterxml/jackson/databind/ser/StdSerializerProvider.java
index 4dc6f86d7..4eeb46b55 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/StdSerializerProvider.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/StdSerializerProvider.java
@@ -570,7 +570,7 @@ public abstract class StdSerializerProvider
* Method called on the actual non-blueprint provider instance object,
* to kick off the serialization.
*/
- protected void _serializeValue(JsonGenerator jgen, Object value)
+ protected final void _serializeValue(JsonGenerator jgen, Object value)
throws IOException, JsonProcessingException
{
JsonSerializer<Object> ser;
@@ -584,11 +584,23 @@ public abstract class StdSerializerProvider
Class<?> cls = value.getClass();
// true, since we do want to cache root-level typed serializers (ditto for null property)
ser = findTypedValueSerializer(cls, true, null);
- // [JACKSON-163]
- wrap = _config.isEnabled(SerializationConfig.Feature.WRAP_ROOT_VALUE);
- if (wrap) {
+
+ // Ok: should we wrap result in an additional property ("root name")?
+ String rootName = _config.getRootName();
+ if (rootName == null) { // not explicitly specified
+ // [JACKSON-163]
+ wrap = _config.isEnabled(SerializationConfig.Feature.WRAP_ROOT_VALUE);
+ if (wrap) {
+ jgen.writeStartObject();
+ jgen.writeFieldName(_rootNames.findRootName(value.getClass(), _config));
+ }
+ } else if (rootName.length() == 0) {
+ wrap = false;
+ } else { // [JACKSON-764]
+ // empty String means explicitly disabled; non-empty that it is enabled
+ wrap = true;
jgen.writeStartObject();
- jgen.writeFieldName(_rootNames.findRootName(value.getClass(), _config));
+ jgen.writeFieldName(rootName);
}
}
try {