Skip to content

Commit fe25f7e

Browse files
committed
Fix #984
1 parent 3bd5de6 commit fe25f7e

File tree

4 files changed

+172
-29
lines changed

4 files changed

+172
-29
lines changed

release-notes/CREDITS

+2
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,8 @@ Ludevik@github:
211211
Antibrumm@github:
212212
* Reported #691: Jackson 2.5.0. NullSerializer for MapProperty failing
213213
(2.5.2)
214+
* Reported #984: JsonStreamContexts are not build the same way for write.. and convert methods
215+
(2.6.4)
214216

215217
Shumpei Akai (flexfrank@github)
216218
* Reported #703: Multiple calls to ObjectMapper#canSerialize(Object.class) returns different values

release-notes/VERSION

+5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ Project: jackson-databind
44
=== Releases ===
55
------------------------------------------------------------------------
66

7+
2.6.4 (not yet released)
8+
9+
#984: JsonStreamContexts are not build the same way for write.. and convert methods
10+
(reported by Antibrumm@github)
11+
712
2.6.3 (12-Oct-2015)
813

914
#749: `EnumMap` serialization ignores `SerializationFeature.WRITE_ENUMS_USING_TO_STRING`

src/main/java/com/fasterxml/jackson/databind/util/TokenBuffer.java

+59-19
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ public TokenBuffer deserialize(JsonParser p, DeserializationContext ctxt) throws
436436
copyCurrentStructure(p);
437437
return this;
438438
}
439-
/* 28-Oct-2014, tatu: As per #592, need to support a special case of starting from
439+
/* 28-Oct-2014, tatu: As per [databind#592], need to support a special case of starting from
440440
* FIELD_NAME, which is taken to mean that we are missing START_OBJECT, but need
441441
* to assume one did exist.
442442
*/
@@ -669,7 +669,7 @@ public void writeString(String text) throws IOException {
669669
if (text == null) {
670670
writeNull();
671671
} else {
672-
_append(JsonToken.VALUE_STRING, text);
672+
_appendValue(JsonToken.VALUE_STRING, text);
673673
}
674674
}
675675

@@ -683,7 +683,7 @@ public void writeString(SerializableString text) throws IOException {
683683
if (text == null) {
684684
writeNull();
685685
} else {
686-
_append(JsonToken.VALUE_STRING, text);
686+
_appendValue(JsonToken.VALUE_STRING, text);
687687
}
688688
}
689689

@@ -728,20 +728,20 @@ public void writeRaw(char c) throws IOException {
728728

729729
@Override
730730
public void writeRawValue(String text) throws IOException {
731-
_append(JsonToken.VALUE_EMBEDDED_OBJECT, new RawValue(text));
731+
_appendValue(JsonToken.VALUE_EMBEDDED_OBJECT, new RawValue(text));
732732
}
733733

734734
@Override
735735
public void writeRawValue(String text, int offset, int len) throws IOException {
736736
if (offset > 0 || len != text.length()) {
737737
text = text.substring(offset, offset+len);
738738
}
739-
_append(JsonToken.VALUE_EMBEDDED_OBJECT, new RawValue(text));
739+
_appendValue(JsonToken.VALUE_EMBEDDED_OBJECT, new RawValue(text));
740740
}
741741

742742
@Override
743743
public void writeRawValue(char[] text, int offset, int len) throws IOException {
744-
_append(JsonToken.VALUE_EMBEDDED_OBJECT, new String(text, offset, len));
744+
_appendValue(JsonToken.VALUE_EMBEDDED_OBJECT, new String(text, offset, len));
745745
}
746746

747747
/*
@@ -752,35 +752,35 @@ public void writeRawValue(char[] text, int offset, int len) throws IOException {
752752

753753
@Override
754754
public void writeNumber(short i) throws IOException {
755-
_append(JsonToken.VALUE_NUMBER_INT, Short.valueOf(i));
755+
_appendValue(JsonToken.VALUE_NUMBER_INT, Short.valueOf(i));
756756
}
757757

758758
@Override
759759
public void writeNumber(int i) throws IOException {
760-
_append(JsonToken.VALUE_NUMBER_INT, Integer.valueOf(i));
760+
_appendValue(JsonToken.VALUE_NUMBER_INT, Integer.valueOf(i));
761761
}
762762

763763
@Override
764764
public void writeNumber(long l) throws IOException {
765-
_append(JsonToken.VALUE_NUMBER_INT, Long.valueOf(l));
765+
_appendValue(JsonToken.VALUE_NUMBER_INT, Long.valueOf(l));
766766
}
767767

768768
@Override
769769
public void writeNumber(double d) throws IOException {
770-
_append(JsonToken.VALUE_NUMBER_FLOAT, Double.valueOf(d));
770+
_appendValue(JsonToken.VALUE_NUMBER_FLOAT, Double.valueOf(d));
771771
}
772772

773773
@Override
774774
public void writeNumber(float f) throws IOException {
775-
_append(JsonToken.VALUE_NUMBER_FLOAT, Float.valueOf(f));
775+
_appendValue(JsonToken.VALUE_NUMBER_FLOAT, Float.valueOf(f));
776776
}
777777

778778
@Override
779779
public void writeNumber(BigDecimal dec) throws IOException {
780780
if (dec == null) {
781781
writeNull();
782782
} else {
783-
_append(JsonToken.VALUE_NUMBER_FLOAT, dec);
783+
_appendValue(JsonToken.VALUE_NUMBER_FLOAT, dec);
784784
}
785785
}
786786

@@ -789,7 +789,7 @@ public void writeNumber(BigInteger v) throws IOException {
789789
if (v == null) {
790790
writeNull();
791791
} else {
792-
_append(JsonToken.VALUE_NUMBER_INT, v);
792+
_appendValue(JsonToken.VALUE_NUMBER_INT, v);
793793
}
794794
}
795795

@@ -798,17 +798,17 @@ public void writeNumber(String encodedValue) throws IOException {
798798
/* 03-Dec-2010, tatu: related to [JACKSON-423], should try to keep as numeric
799799
* identity as long as possible
800800
*/
801-
_append(JsonToken.VALUE_NUMBER_FLOAT, encodedValue);
801+
_appendValue(JsonToken.VALUE_NUMBER_FLOAT, encodedValue);
802802
}
803803

804804
@Override
805805
public void writeBoolean(boolean state) throws IOException {
806-
_append(state ? JsonToken.VALUE_TRUE : JsonToken.VALUE_FALSE);
806+
_appendValue(state ? JsonToken.VALUE_TRUE : JsonToken.VALUE_FALSE);
807807
}
808808

809809
@Override
810810
public void writeNull() throws IOException {
811-
_append(JsonToken.VALUE_NULL);
811+
_appendValue(JsonToken.VALUE_NULL);
812812
}
813813

814814
/*
@@ -826,15 +826,15 @@ public void writeObject(Object value) throws IOException
826826
}
827827
Class<?> raw = value.getClass();
828828
if (raw == byte[].class || (value instanceof RawValue)) {
829-
_append(JsonToken.VALUE_EMBEDDED_OBJECT, value);
829+
_appendValue(JsonToken.VALUE_EMBEDDED_OBJECT, value);
830830
return;
831831
}
832832
if (_objectCodec == null) {
833833
/* 28-May-2014, tatu: Tricky choice here; if no codec, should we
834834
* err out, or just embed? For now, do latter.
835835
*/
836836
// throw new JsonMappingException("No ObjectCodec configured for TokenBuffer, writeObject() called");
837-
_append(JsonToken.VALUE_EMBEDDED_OBJECT, value);
837+
_appendValue(JsonToken.VALUE_EMBEDDED_OBJECT, value);
838838
} else {
839839
_objectCodec.writeValue(this, value);
840840
}
@@ -850,7 +850,7 @@ public void writeTree(TreeNode node) throws IOException
850850

851851
if (_objectCodec == null) {
852852
// as with 'writeObject()', is codec optional?
853-
_append(JsonToken.VALUE_EMBEDDED_OBJECT, node);
853+
_appendValue(JsonToken.VALUE_EMBEDDED_OBJECT, node);
854854
} else {
855855
_objectCodec.writeTree(this, node);
856856
}
@@ -1082,6 +1082,46 @@ protected final void _append(JsonToken type, Object value)
10821082
}
10831083
}
10841084

1085+
/**
1086+
* Similar to {@link #_append(JsonToken)} but also updates context with
1087+
* knowledge that a scalar value was written
1088+
*
1089+
* @since 2.6.4
1090+
*/
1091+
protected final void _appendValue(JsonToken type)
1092+
{
1093+
_writeContext.writeValue();
1094+
Segment next = _hasNativeId
1095+
? _last.append(_appendAt, type, _objectId, _typeId)
1096+
: _last.append(_appendAt, type);
1097+
if (next == null) {
1098+
++_appendAt;
1099+
} else {
1100+
_last = next;
1101+
_appendAt = 1; // since we added first at 0
1102+
}
1103+
}
1104+
1105+
/**
1106+
* Similar to {@link #_append(JsonToken,Object)} but also updates context with
1107+
* knowledge that a scalar value was written
1108+
*
1109+
* @since 2.6.4
1110+
*/
1111+
protected final void _appendValue(JsonToken type, Object value)
1112+
{
1113+
_writeContext.writeValue();
1114+
Segment next = _hasNativeId
1115+
? _last.append(_appendAt, type, value, _objectId, _typeId)
1116+
: _last.append(_appendAt, type, value);
1117+
if (next == null) {
1118+
++_appendAt;
1119+
} else {
1120+
_last = next;
1121+
_appendAt = 1;
1122+
}
1123+
}
1124+
10851125
protected final void _appendRaw(int rawType, Object value)
10861126
{
10871127
Segment next = _hasNativeId

src/test/java/com/fasterxml/jackson/databind/util/TestTokenBuffer.java

+106-10
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
public class TestTokenBuffer extends BaseMapTest
1212
{
13+
private final ObjectMapper MAPPER = objectMapper();
14+
1315
/*
1416
/**********************************************************
1517
/* Basic TokenBuffer tests
@@ -219,8 +221,6 @@ public void testAppend() throws IOException
219221
// deal with
220222
public void testWithUUID() throws IOException
221223
{
222-
ObjectMapper mapper = new ObjectMapper();
223-
224224
for (String value : new String[] {
225225
"00000007-0000-0000-0000-000000000000",
226226
"76e6d183-5f68-4afa-b94a-922c1fdb83f8",
@@ -229,24 +229,121 @@ public void testWithUUID() throws IOException
229229
"591b2869-146e-41d7-8048-e8131f1fdec5",
230230
"82994ac2-7b23-49f2-8cc5-e24cf6ed77be",
231231
}) {
232-
TokenBuffer buf = new TokenBuffer(mapper, false); // no ObjectCodec
232+
TokenBuffer buf = new TokenBuffer(MAPPER, false); // no ObjectCodec
233233
UUID uuid = UUID.fromString(value);
234-
mapper.writeValue(buf, uuid);
234+
MAPPER.writeValue(buf, uuid);
235235
buf.close();
236236

237237
// and bring it back
238-
UUID out = mapper.readValue(buf.asParser(), UUID.class);
238+
UUID out = MAPPER.readValue(buf.asParser(), UUID.class);
239239
assertEquals(uuid.toString(), out.toString());
240240

241-
// second part: As per [#362], should NOT use binary with TokenBuffer
241+
// second part: As per [databind#362], should NOT use binary with TokenBuffer
242242
JsonParser jp = buf.asParser();
243243
assertEquals(JsonToken.VALUE_STRING, jp.nextToken());
244244
String str = jp.getText();
245245
assertEquals(value, str);
246246
jp.close();
247247
}
248248
}
249-
249+
250+
/*
251+
/**********************************************************
252+
/* Tests for read/output contexts
253+
/**********************************************************
254+
*/
255+
256+
// for [databind#984]: ensure output context handling identical
257+
public void testOutputContext() throws IOException
258+
{
259+
TokenBuffer buf = new TokenBuffer(null, false); // no ObjectCodec
260+
StringWriter w = new StringWriter();
261+
JsonGenerator gen = MAPPER.getFactory().createGenerator(w);
262+
263+
// test content: [{"a":1,"b":{"c":2}},{"a":2,"b":{"c":3}}]
264+
265+
buf.writeStartArray();
266+
gen.writeStartArray();
267+
_verifyOutputContext(buf, gen);
268+
269+
buf.writeStartObject();
270+
gen.writeStartObject();
271+
_verifyOutputContext(buf, gen);
272+
273+
buf.writeFieldName("a");
274+
gen.writeFieldName("a");
275+
_verifyOutputContext(buf, gen);
276+
277+
buf.writeNumber(1);
278+
gen.writeNumber(1);
279+
_verifyOutputContext(buf, gen);
280+
281+
buf.writeFieldName("b");
282+
gen.writeFieldName("b");
283+
_verifyOutputContext(buf, gen);
284+
285+
buf.writeStartObject();
286+
gen.writeStartObject();
287+
_verifyOutputContext(buf, gen);
288+
289+
buf.writeFieldName("c");
290+
gen.writeFieldName("c");
291+
_verifyOutputContext(buf, gen);
292+
293+
buf.writeNumber(2);
294+
gen.writeNumber(2);
295+
_verifyOutputContext(buf, gen);
296+
297+
buf.writeEndObject();
298+
gen.writeEndObject();
299+
_verifyOutputContext(buf, gen);
300+
301+
buf.writeEndObject();
302+
gen.writeEndObject();
303+
_verifyOutputContext(buf, gen);
304+
305+
buf.writeEndArray();
306+
gen.writeEndArray();
307+
_verifyOutputContext(buf, gen);
308+
309+
buf.close();
310+
gen.close();
311+
}
312+
313+
private void _verifyOutputContext(JsonGenerator gen1, JsonGenerator gen2)
314+
{
315+
_verifyOutputContext(gen1.getOutputContext(), gen2.getOutputContext());
316+
}
317+
318+
private void _verifyOutputContext(JsonStreamContext ctxt1, JsonStreamContext ctxt2)
319+
{
320+
if (ctxt1 == null) {
321+
if (ctxt2 == null) {
322+
return;
323+
}
324+
fail("Context 1 null, context 2 not null: "+ctxt2);
325+
} else if (ctxt2 == null) {
326+
fail("Context 2 null, context 1 not null: "+ctxt1);
327+
}
328+
if (!ctxt1.getTypeDesc().equals(ctxt2.getTypeDesc())) {
329+
fail("Different output context: token-buffer's = "+ctxt1+", json-generator's: "+ctxt2);
330+
}
331+
332+
if (ctxt1.inObject()) {
333+
assertTrue(ctxt2.inObject());
334+
String str1 = ctxt1.getCurrentName();
335+
String str2 = ctxt2.getCurrentName();
336+
337+
if ((str1 != str2) && !str1.equals(str2)) {
338+
fail("Expected name '"+str2+"' (JsonParser), TokenBuffer had '"+str1+"'");
339+
}
340+
} else if (ctxt1.inArray()) {
341+
assertTrue(ctxt2.inArray());
342+
assertEquals(ctxt1.getCurrentIndex(), ctxt2.getCurrentIndex());
343+
}
344+
_verifyOutputContext(ctxt1.getParent(), ctxt2.getParent());
345+
}
346+
250347
/*
251348
/**********************************************************
252349
/* Tests to verify interaction of TokenBuffer and JsonParserSequence
@@ -335,7 +432,7 @@ public void testWithMultipleJsonParserSequences() throws IOException
335432
buf4.close();
336433
}
337434

338-
// [Issue#743]
435+
// [databind#743]
339436
public void testRawValues() throws Exception
340437
{
341438
final String RAW = "{\"a\":1}";
@@ -350,7 +447,6 @@ public void testRawValues() throws Exception
350447
buf.close();
351448

352449
// then verify it would be serialized just fine
353-
ObjectMapper mapper = objectMapper();
354-
assertEquals(RAW, mapper.writeValueAsString(buf));
450+
assertEquals(RAW, MAPPER.writeValueAsString(buf));
355451
}
356452
}

0 commit comments

Comments
 (0)