Skip to content

Commit 4670d4b

Browse files
committed
Implement #743
1 parent 31b3b2b commit 4670d4b

File tree

4 files changed

+154
-34
lines changed

4 files changed

+154
-34
lines changed

release-notes/VERSION

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Project: jackson-databind
1414
(reported by wealdtech@github)
1515
#725: Auto-detect multi-argument constructor with implicit names if it is the only visible creator
1616
#727: Improve `ObjectWriter.forType()` to avoid forcing base type for container types
17+
#743: Add `RawValue` helper type, for piping raw values through `TokenBuffer`
1718
- Remove old cglib compatibility tests; cause problems in Eclipse
1819

1920
2.5.3 (not yet released)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package com.fasterxml.jackson.databind.util;
2+
3+
import java.io.IOException;
4+
5+
import com.fasterxml.jackson.core.JsonGenerator;
6+
import com.fasterxml.jackson.core.SerializableString;
7+
import com.fasterxml.jackson.databind.JsonSerializable;
8+
import com.fasterxml.jackson.databind.SerializerProvider;
9+
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
10+
11+
/**
12+
* Helper class used to encapsulate "raw values", pre-encoded textual content
13+
* that can be output as opaque value with no quoting/escaping, using
14+
* {@link com.fasterxml.jackson.core.JsonGenerator#writeRawValue(String)}.
15+
* It may be stored in {@link TokenBuffer}, as well as in Tree Model
16+
* ({@link com.fasterxml.jackson.databind.JsonNode})
17+
*
18+
* @since 2.6
19+
*/
20+
public class RawValue
21+
implements JsonSerializable
22+
{
23+
protected Object _value;
24+
25+
public RawValue(String v) {
26+
_value = v;
27+
}
28+
29+
public RawValue(SerializableString v) {
30+
_value = v;
31+
}
32+
33+
public RawValue(JsonSerializable v) {
34+
_value = v;
35+
}
36+
37+
protected RawValue(Object value, boolean bogus) {
38+
_value = value;
39+
}
40+
41+
/**
42+
* Accessor for returning enclosed raw value in whatever form it was created in
43+
* (usually {@link java.lang.String}, {link SerializableString}, or any {@link JsonSerializable}).
44+
*/
45+
public Object rawValue() {
46+
return _value;
47+
}
48+
49+
@Override
50+
public void serialize(JsonGenerator gen, SerializerProvider serializers) throws IOException
51+
{
52+
if (_value instanceof JsonSerializable) {
53+
((JsonSerializable) _value).serialize(gen, serializers);
54+
} else {
55+
_serialize(gen);
56+
}
57+
}
58+
59+
@Override
60+
public void serializeWithType(JsonGenerator gen, SerializerProvider serializers,
61+
TypeSerializer typeSer) throws IOException
62+
{
63+
if (_value instanceof JsonSerializable) {
64+
((JsonSerializable) _value).serializeWithType(gen, serializers, typeSer);
65+
} else if (_value instanceof SerializableString) {
66+
/* Since these are not really to be deserialized (with or without type info),
67+
* just re-route as regular write, which will create one... hopefully it works
68+
*/
69+
serialize(gen, serializers);
70+
}
71+
}
72+
73+
public void serialize(JsonGenerator gen) throws IOException
74+
{
75+
if (_value instanceof JsonSerializable) {
76+
// No SerializerProvider passed, must go via generator, callback
77+
gen.writeObject(_value);
78+
} else {
79+
_serialize(gen);
80+
}
81+
}
82+
83+
protected void _serialize(JsonGenerator gen) throws IOException
84+
{
85+
if (_value instanceof SerializableString) {
86+
gen.writeRawValue((SerializableString) _value);
87+
} else {
88+
gen.writeRawValue(String.valueOf(_value));
89+
}
90+
}
91+
}

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

+43-34
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ public TokenBuffer append(TokenBuffer other) throws IOException
277277
* references (from core to mapper package); and as such we also
278278
* can not take second argument.
279279
*/
280-
public void serialize(JsonGenerator jgen) throws IOException
280+
public void serialize(JsonGenerator gen) throws IOException
281281
{
282282
Segment segment = _first;
283283
int ptr = -1;
@@ -298,94 +298,101 @@ public void serialize(JsonGenerator jgen) throws IOException
298298
if (hasIds) {
299299
Object id = segment.findObjectId(ptr);
300300
if (id != null) {
301-
jgen.writeObjectId(id);
301+
gen.writeObjectId(id);
302302
}
303303
id = segment.findTypeId(ptr);
304304
if (id != null) {
305-
jgen.writeTypeId(id);
305+
gen.writeTypeId(id);
306306
}
307307
}
308308

309309
// Note: copied from 'copyCurrentEvent'...
310310
switch (t) {
311311
case START_OBJECT:
312-
jgen.writeStartObject();
312+
gen.writeStartObject();
313313
break;
314314
case END_OBJECT:
315-
jgen.writeEndObject();
315+
gen.writeEndObject();
316316
break;
317317
case START_ARRAY:
318-
jgen.writeStartArray();
318+
gen.writeStartArray();
319319
break;
320320
case END_ARRAY:
321-
jgen.writeEndArray();
321+
gen.writeEndArray();
322322
break;
323323
case FIELD_NAME:
324324
{
325325
// 13-Dec-2010, tatu: Maybe we should start using different type tokens to reduce casting?
326326
Object ob = segment.get(ptr);
327327
if (ob instanceof SerializableString) {
328-
jgen.writeFieldName((SerializableString) ob);
328+
gen.writeFieldName((SerializableString) ob);
329329
} else {
330-
jgen.writeFieldName((String) ob);
330+
gen.writeFieldName((String) ob);
331331
}
332332
}
333333
break;
334334
case VALUE_STRING:
335335
{
336336
Object ob = segment.get(ptr);
337337
if (ob instanceof SerializableString) {
338-
jgen.writeString((SerializableString) ob);
338+
gen.writeString((SerializableString) ob);
339339
} else {
340-
jgen.writeString((String) ob);
340+
gen.writeString((String) ob);
341341
}
342342
}
343343
break;
344344
case VALUE_NUMBER_INT:
345345
{
346346
Object n = segment.get(ptr);
347347
if (n instanceof Integer) {
348-
jgen.writeNumber((Integer) n);
348+
gen.writeNumber((Integer) n);
349349
} else if (n instanceof BigInteger) {
350-
jgen.writeNumber((BigInteger) n);
350+
gen.writeNumber((BigInteger) n);
351351
} else if (n instanceof Long) {
352-
jgen.writeNumber((Long) n);
352+
gen.writeNumber((Long) n);
353353
} else if (n instanceof Short) {
354-
jgen.writeNumber((Short) n);
354+
gen.writeNumber((Short) n);
355355
} else {
356-
jgen.writeNumber(((Number) n).intValue());
356+
gen.writeNumber(((Number) n).intValue());
357357
}
358358
}
359359
break;
360360
case VALUE_NUMBER_FLOAT:
361361
{
362362
Object n = segment.get(ptr);
363363
if (n instanceof Double) {
364-
jgen.writeNumber(((Double) n).doubleValue());
364+
gen.writeNumber(((Double) n).doubleValue());
365365
} else if (n instanceof BigDecimal) {
366-
jgen.writeNumber((BigDecimal) n);
366+
gen.writeNumber((BigDecimal) n);
367367
} else if (n instanceof Float) {
368-
jgen.writeNumber(((Float) n).floatValue());
368+
gen.writeNumber(((Float) n).floatValue());
369369
} else if (n == null) {
370-
jgen.writeNull();
370+
gen.writeNull();
371371
} else if (n instanceof String) {
372-
jgen.writeNumber((String) n);
372+
gen.writeNumber((String) n);
373373
} else {
374374
throw new JsonGenerationException("Unrecognized value type for VALUE_NUMBER_FLOAT: "+n.getClass().getName()+", can not serialize");
375375
}
376376
}
377377
break;
378378
case VALUE_TRUE:
379-
jgen.writeBoolean(true);
379+
gen.writeBoolean(true);
380380
break;
381381
case VALUE_FALSE:
382-
jgen.writeBoolean(false);
382+
gen.writeBoolean(false);
383383
break;
384384
case VALUE_NULL:
385-
jgen.writeNull();
385+
gen.writeNull();
386386
break;
387387
case VALUE_EMBEDDED_OBJECT:
388-
jgen.writeObject(segment.get(ptr));
388+
{
389+
Object value = segment.get(ptr);
390+
if (value instanceof RawValue) {
391+
((RawValue) value).serialize(gen);
392+
} else {
393+
gen.writeObject(value);
394+
}
395+
}
389396
break;
390397
default:
391398
throw new RuntimeException("Internal error: should never end up through this code path");
@@ -656,16 +663,14 @@ public void writeString(SerializableString text) throws IOException {
656663
}
657664

658665
@Override
659-
public void writeRawUTF8String(byte[] text, int offset, int length)
660-
throws IOException
666+
public void writeRawUTF8String(byte[] text, int offset, int length) throws IOException
661667
{
662668
// could add support for buffering if we really want it...
663669
_reportUnsupportedOperation();
664670
}
665671

666672
@Override
667-
public void writeUTF8String(byte[] text, int offset, int length)
668-
throws IOException
673+
public void writeUTF8String(byte[] text, int offset, int length) throws IOException
669674
{
670675
// could add support for buffering if we really want it...
671676
_reportUnsupportedOperation();
@@ -698,17 +703,20 @@ public void writeRaw(char c) throws IOException {
698703

699704
@Override
700705
public void writeRawValue(String text) throws IOException {
701-
_reportUnsupportedOperation();
706+
_append(JsonToken.VALUE_EMBEDDED_OBJECT, new RawValue(text));
702707
}
703708

704709
@Override
705710
public void writeRawValue(String text, int offset, int len) throws IOException {
706-
_reportUnsupportedOperation();
711+
if (offset > 0 || len != text.length()) {
712+
text = text.substring(offset, offset+len);
713+
}
714+
_append(JsonToken.VALUE_EMBEDDED_OBJECT, new RawValue(text));
707715
}
708716

709717
@Override
710718
public void writeRawValue(char[] text, int offset, int len) throws IOException {
711-
_reportUnsupportedOperation();
719+
_append(JsonToken.VALUE_EMBEDDED_OBJECT, new String(text, offset, len));
712720
}
713721

714722
/*
@@ -792,10 +800,11 @@ public void writeObject(Object value) throws IOException
792800
return;
793801
}
794802
Class<?> raw = value.getClass();
795-
if (raw == byte[].class) {
803+
if (raw == byte[].class || (value instanceof RawValue)) {
796804
_append(JsonToken.VALUE_EMBEDDED_OBJECT, value);
797805
return;
798-
} else if (_objectCodec == null) {
806+
}
807+
if (_objectCodec == null) {
799808
/* 28-May-2014, tatu: Tricky choice here; if no codec, should we
800809
* err out, or just embed? For now, do latter.
801810
*/

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

+19
Original file line numberDiff line numberDiff line change
@@ -334,4 +334,23 @@ public void testWithMultipleJsonParserSequences() throws IOException
334334
buf3.close();
335335
buf4.close();
336336
}
337+
338+
// [Issue#743]
339+
public void testRawValues() throws Exception
340+
{
341+
final String RAW = "{\"a\":1}";
342+
TokenBuffer buf = new TokenBuffer(null, false);
343+
buf.writeRawValue(RAW);
344+
// first: raw value won't be transformed in any way:
345+
JsonParser p = buf.asParser();
346+
assertToken(JsonToken.VALUE_EMBEDDED_OBJECT, p.nextToken());
347+
assertEquals(RawValue.class, p.getEmbeddedObject().getClass());
348+
assertNull(p.nextToken());
349+
p.close();
350+
buf.close();
351+
352+
// then verify it would be serialized just fine
353+
ObjectMapper mapper = objectMapper();
354+
assertEquals(RAW, mapper.writeValueAsString(buf));
355+
}
337356
}

0 commit comments

Comments
 (0)