Skip to content

Commit a199cbd

Browse files
committed
Refactoring to make ObjectWriter more robust
1 parent f144070 commit a199cbd

File tree

3 files changed

+149
-67
lines changed

3 files changed

+149
-67
lines changed

src/main/java/com/fasterxml/jackson/databind/ObjectWriter.java

+108-67
Original file line numberDiff line numberDiff line change
@@ -76,27 +76,14 @@ public class ObjectWriter
7676
* @since 2.1
7777
*/
7878
protected final JsonSerializer<Object> _rootSerializer;
79-
80-
/**
81-
* To allow for dynamic enabling/disabling of pretty printing,
82-
* pretty printer can be optionally configured for writer
83-
* as well
84-
*/
85-
protected final PrettyPrinter _prettyPrinter;
86-
87-
/**
88-
* When using data format that uses a schema, schema is passed
89-
* to generator.
90-
*/
91-
protected final FormatSchema _schema;
92-
79+
9380
/**
94-
* Caller may want to specify character escaping details, either as
95-
* defaults, or on call-by-call basis.
96-
*
97-
* @since 2.3
81+
* Container for settings that need to be passed to {@link JsonGenerator}
82+
* constructed for serializing values.
83+
*
84+
* @since 2.5
9885
*/
99-
protected final CharacterEscapes _characterEscapes;
86+
protected final GeneratorSettings _generatorSettings;
10087

10188
/*
10289
/**********************************************************
@@ -114,9 +101,8 @@ protected ObjectWriter(ObjectMapper mapper, SerializationConfig config,
114101
_serializerProvider = mapper._serializerProvider;
115102
_serializerFactory = mapper._serializerFactory;
116103
_generatorFactory = mapper._jsonFactory;
117-
_prettyPrinter = pp;
118-
_schema = null;
119-
_characterEscapes = null;
104+
_generatorSettings = (pp == null) ? GeneratorSettings.empty
105+
: new GeneratorSettings(pp, null, null);
120106

121107
// 29-Apr-2014, tatu: There is no "untyped serializer", so:
122108
if (rootType == null || rootType.hasRawClass(Object.class)) {
@@ -140,9 +126,7 @@ protected ObjectWriter(ObjectMapper mapper, SerializationConfig config)
140126

141127
_rootType = null;
142128
_rootSerializer = null;
143-
_prettyPrinter = null;
144-
_schema = null;
145-
_characterEscapes = null;
129+
_generatorSettings = GeneratorSettings.empty;
146130
}
147131

148132
/**
@@ -159,17 +143,16 @@ protected ObjectWriter(ObjectMapper mapper, SerializationConfig config,
159143

160144
_rootType = null;
161145
_rootSerializer = null;
162-
_prettyPrinter = null;
163-
_schema = s;
164-
_characterEscapes = null;
146+
_generatorSettings = (s == null) ? GeneratorSettings.empty
147+
: new GeneratorSettings(null, s, null);
165148
}
166149

167150
/**
168151
* Copy constructor used for building variations.
169152
*/
170153
protected ObjectWriter(ObjectWriter base, SerializationConfig config,
171154
JavaType rootType, JsonSerializer<Object> rootSer,
172-
PrettyPrinter pp, FormatSchema s, CharacterEscapes escapes)
155+
GeneratorSettings genSettings)
173156
{
174157
_config = config;
175158

@@ -179,9 +162,7 @@ protected ObjectWriter(ObjectWriter base, SerializationConfig config,
179162

180163
_rootType = rootType;
181164
_rootSerializer = rootSer;
182-
_prettyPrinter = pp;
183-
_schema = s;
184-
_characterEscapes = escapes;
165+
_generatorSettings = genSettings;
185166
}
186167

187168
/**
@@ -194,12 +175,10 @@ protected ObjectWriter(ObjectWriter base, SerializationConfig config)
194175
_serializerProvider = base._serializerProvider;
195176
_serializerFactory = base._serializerFactory;
196177
_generatorFactory = base._generatorFactory;
197-
_schema = base._schema;
198-
_characterEscapes = base._characterEscapes;
178+
_generatorSettings = base._generatorSettings;
199179

200180
_rootType = base._rootType;
201181
_rootSerializer = base._rootSerializer;
202-
_prettyPrinter = base._prettyPrinter;
203182
}
204183

205184
/**
@@ -214,12 +193,10 @@ protected ObjectWriter(ObjectWriter base, JsonFactory f)
214193
_serializerProvider = base._serializerProvider;
215194
_serializerFactory = base._serializerFactory;
216195
_generatorFactory = base._generatorFactory;
217-
_schema = base._schema;
218-
_characterEscapes = base._characterEscapes;
196+
_generatorSettings = base._generatorSettings;
219197

220198
_rootType = base._rootType;
221199
_rootSerializer = base._rootSerializer;
222-
_prettyPrinter = base._prettyPrinter;
223200
}
224201

225202
/**
@@ -258,14 +235,15 @@ protected ObjectWriter _new(ObjectWriter base, SerializationConfig config) {
258235
}
259236

260237
/**
261-
* Overridable factory method called by various "withXxx()" methods
238+
* Overridable factory method called by various "withXxx()" methods.
239+
* It assumes `this` as base for settings other than those directly
240+
* passed in.
262241
*
263242
* @since 2.5
264243
*/
265-
protected ObjectWriter _new(ObjectWriter base, SerializationConfig config,
266-
JavaType rootType, JsonSerializer<Object> rootSer,
267-
PrettyPrinter pp, FormatSchema s, CharacterEscapes escapes) {
268-
return new ObjectWriter(base, config, rootType, rootSer, pp, s, escapes);
244+
protected ObjectWriter _new(JavaType rootType, JsonSerializer<Object> rootSer,
245+
GeneratorSettings genSettings) {
246+
return new ObjectWriter(this, _config, rootType, rootSer, genSettings);
269247
}
270248

271249
/**
@@ -423,15 +401,11 @@ public ObjectWriter with(FilterProvider filterProvider) {
423401
* printer (or, if null, will not do any pretty-printing)
424402
*/
425403
public ObjectWriter with(PrettyPrinter pp) {
426-
if (pp == _prettyPrinter) {
404+
GeneratorSettings genSet = _generatorSettings.with(pp);
405+
if (genSet == _generatorSettings) {
427406
return this;
428407
}
429-
// since null would mean "don't care", need to use placeholder to indicate "disable"
430-
if (pp == null) {
431-
pp = NULL_PRETTY_PRINTER;
432-
}
433-
return _new(this, _config, _rootType, _rootSerializer,
434-
pp, _schema, _characterEscapes);
408+
return _new(_rootType, _rootSerializer, genSet);
435409
}
436410

437411
/**
@@ -455,12 +429,12 @@ public ObjectWriter withRootName(String rootName) {
455429
* rather construct and returns a newly configured instance.
456430
*/
457431
public ObjectWriter with(FormatSchema schema) {
458-
if (_schema == schema) {
432+
GeneratorSettings genSet = _generatorSettings.with(schema);
433+
if (genSet == _generatorSettings) {
459434
return this;
460435
}
461436
_verifySchemaType(schema);
462-
return _new(this, _config, _rootType, _rootSerializer,
463-
_prettyPrinter, schema, _characterEscapes);
437+
return _new(_rootType, _rootSerializer, genSet);
464438
}
465439

466440
/**
@@ -492,8 +466,7 @@ public ObjectWriter forType(JavaType rootType)
492466
rootType = rootType.withStaticTyping();
493467
rootSer = _prefetchRootSerializer(_config, rootType);
494468
}
495-
return _new(this, _config, rootType, rootSer,
496-
_prettyPrinter, _schema, _characterEscapes);
469+
return _new(rootType, rootSer, _generatorSettings);
497470
}
498471

499472
/**
@@ -576,11 +549,11 @@ public ObjectWriter with(Base64Variant b64variant) {
576549
* @since 2.3
577550
*/
578551
public ObjectWriter with(CharacterEscapes escapes) {
579-
if (_characterEscapes == escapes) {
552+
GeneratorSettings genSet = _generatorSettings.with(escapes);
553+
if (genSet == _generatorSettings) {
580554
return this;
581555
}
582-
return _new(this, _config, _rootType, _rootSerializer,
583-
_prettyPrinter, _schema, escapes);
556+
return _new(_rootType, _rootSerializer, genSet);
584557
}
585558

586559
/**
@@ -1011,21 +984,21 @@ public boolean canSerialize(Class<?> type) {
1011984
public boolean canSerialize(Class<?> type, AtomicReference<Throwable> cause) {
1012985
return _serializerProvider(_config).hasSerializerFor(type, cause);
1013986
}
1014-
987+
1015988
/*
1016989
/**********************************************************
1017990
/* Overridable helper methods
1018991
/**********************************************************
1019992
*/
1020-
993+
1021994
/**
1022995
* Overridable helper method used for constructing
1023996
* {@link SerializerProvider} to use for serialization.
1024997
*/
1025998
protected DefaultSerializerProvider _serializerProvider(SerializationConfig config) {
1026999
return _serializerProvider.createInstance(config, _serializerFactory);
10271000
}
1028-
1001+
10291002
/*
10301003
/**********************************************************
10311004
/* Internal methods
@@ -1192,8 +1165,9 @@ protected void _configureJsonGenerator(JsonGenerator gen) {
11921165
*/
11931166
protected JsonGenerator _configureGenerator(JsonGenerator gen)
11941167
{
1195-
if (_prettyPrinter != null) {
1196-
PrettyPrinter pp = _prettyPrinter;
1168+
GeneratorSettings genSet = _generatorSettings;
1169+
PrettyPrinter pp = genSet.prettyPrinter;
1170+
if (pp != null) {
11971171
if (pp == NULL_PRETTY_PRINTER) {
11981172
gen.setPrettyPrinter(null);
11991173
} else {
@@ -1208,14 +1182,81 @@ protected JsonGenerator _configureGenerator(JsonGenerator gen)
12081182
} else if (_config.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
12091183
gen.useDefaultPrettyPrinter();
12101184
}
1211-
if (_characterEscapes != null) {
1212-
gen.setCharacterEscapes(_characterEscapes);
1185+
CharacterEscapes esc = genSet.characterEscapes;
1186+
if (esc != null) {
1187+
gen.setCharacterEscapes(esc);
12131188
}
12141189
// [JACKSON-520]: add support for pass-through schema:
1215-
if (_schema != null) {
1216-
gen.setSchema(_schema);
1190+
FormatSchema sch = genSet.schema;
1191+
if (sch != null) {
1192+
gen.setSchema(sch);
12171193
}
12181194
_config.initialize(gen); // since 2.5
12191195
return gen;
12201196
}
1197+
1198+
/*
1199+
/**********************************************************
1200+
/* Helper classes for configuration
1201+
/**********************************************************
1202+
*/
1203+
1204+
/**
1205+
* Helper class used for containing settings specifically related
1206+
* to (re)configuring {@link JsonGenerator} constructed for
1207+
* writing output.
1208+
*
1209+
* @since 2.5
1210+
*/
1211+
public final static class GeneratorSettings
1212+
implements java.io.Serializable
1213+
{
1214+
private static final long serialVersionUID = 1L;
1215+
1216+
public final static GeneratorSettings empty = new GeneratorSettings(null, null, null);
1217+
1218+
/**
1219+
* To allow for dynamic enabling/disabling of pretty printing,
1220+
* pretty printer can be optionally configured for writer
1221+
* as well
1222+
*/
1223+
public final PrettyPrinter prettyPrinter;
1224+
1225+
/**
1226+
* When using data format that uses a schema, schema is passed
1227+
* to generator.
1228+
*/
1229+
public final FormatSchema schema;
1230+
1231+
/**
1232+
* Caller may want to specify character escaping details, either as
1233+
* defaults, or on call-by-call basis.
1234+
*/
1235+
public final CharacterEscapes characterEscapes;
1236+
1237+
public GeneratorSettings(PrettyPrinter pp, FormatSchema sch, CharacterEscapes esc) {
1238+
prettyPrinter = pp;
1239+
schema = sch;
1240+
characterEscapes = esc;
1241+
}
1242+
1243+
public GeneratorSettings with(PrettyPrinter pp) {
1244+
// since null would mean "don't care", need to use placeholder to indicate "disable"
1245+
if (pp == null) {
1246+
pp = NULL_PRETTY_PRINTER;
1247+
}
1248+
return (pp == prettyPrinter) ? this
1249+
: new GeneratorSettings(pp, schema, characterEscapes);
1250+
}
1251+
1252+
public GeneratorSettings with(FormatSchema sch) {
1253+
return (schema == sch) ? this
1254+
: new GeneratorSettings(prettyPrinter, sch, characterEscapes);
1255+
}
1256+
1257+
public GeneratorSettings with(CharacterEscapes esc) {
1258+
return (characterEscapes == esc) ? this
1259+
: new GeneratorSettings(prettyPrinter, schema, esc);
1260+
}
1261+
}
12211262
}

src/test/java/com/fasterxml/jackson/databind/seq/SequenceWriterTest.java

+14
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,18 @@ public void testPolymorphicArrayWithoutType() throws Exception
101101
assertEquals(aposToQuotes("[{'type':'A','value':-1},{'type':'A','value':6}]"),
102102
strw.toString());
103103
}
104+
105+
@SuppressWarnings("resource")
106+
public void testPolymorphicArrayWithType() throws Exception
107+
{
108+
StringWriter strw = new StringWriter();
109+
SequenceWriter w = WRITER
110+
.forType(PolyBase.class)
111+
.writeValuesAsArray(strw);
112+
w.write(new ImplA(-1))
113+
.write(new ImplA(6))
114+
.close();
115+
assertEquals(aposToQuotes("[{'type':'A','value':-1},{'type':'A','value':6}]"),
116+
strw.toString());
117+
}
104118
}

src/test/java/com/fasterxml/jackson/databind/ser/TestObjectWriter.java

+27
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
import java.util.*;
44

5+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
6+
import com.fasterxml.jackson.annotation.JsonTypeName;
7+
58
import com.fasterxml.jackson.core.*;
69
import com.fasterxml.jackson.databind.*;
710
import com.fasterxml.jackson.databind.node.ObjectNode;
@@ -15,6 +18,23 @@ public class TestObjectWriter
1518
{
1619
final ObjectMapper MAPPER = new ObjectMapper();
1720

21+
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
22+
static class PolyBase {
23+
}
24+
25+
@JsonTypeName("A")
26+
static class ImplA extends PolyBase {
27+
public int value;
28+
29+
public ImplA(int v) { value = v; }
30+
}
31+
32+
/*
33+
/**********************************************************
34+
/* Test methods
35+
/**********************************************************
36+
*/
37+
1838
public void testPrettyPrinter() throws Exception
1939
{
2040
ObjectWriter writer = MAPPER.writer();
@@ -64,4 +84,11 @@ public void testObjectWriterWithNode() throws Exception
6484
String json = writer.writeValueAsString(stuff);
6585
assertEquals("{\"a\":5}", json);
6686
}
87+
88+
public void testPolymorpicWithTyping() throws Exception
89+
{
90+
ObjectWriter writer = MAPPER.writerFor(PolyBase.class);
91+
String json = writer.writeValueAsString(new ImplA(3));
92+
assertEquals(aposToQuotes("{'type':'A':'value':3}"), json);
93+
}
6794
}

0 commit comments

Comments
 (0)