Skip to content

Commit 9afe389

Browse files
Add JsonGenerator.copyCurrentStructureExact() (#1470)
follows the format of copyCurrentEvent/copyCurrentEventExact
1 parent 7ae2b8b commit 9afe389

File tree

2 files changed

+138
-0
lines changed

2 files changed

+138
-0
lines changed

src/main/java/com/fasterxml/jackson/core/JsonGenerator.java

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2690,6 +2690,46 @@ public void copyCurrentStructure(JsonParser p) throws IOException
26902690
}
26912691
}
26922692

2693+
/**
2694+
* Same as {@link #copyCurrentStructure} with the exception that copying of numeric
2695+
* values tries to avoid any conversion losses; in particular for floating-point
2696+
* numbers. This usually matters when transcoding from textual format like JSON
2697+
* to a binary format.
2698+
* See {@link #_copyCurrentFloatValueExact} for details.
2699+
*
2700+
* @param p Parser that points to the value to copy
2701+
*
2702+
* @throws IOException if there is either an underlying I/O problem or encoding
2703+
* issue at format layer
2704+
*
2705+
* @since 2.21
2706+
*/
2707+
public void copyCurrentStructureExact(JsonParser p) throws IOException
2708+
{
2709+
JsonToken t = p.currentToken();
2710+
// Let's handle field-name separately first
2711+
int id = (t == null) ? ID_NOT_AVAILABLE : t.id();
2712+
if (id == ID_FIELD_NAME) {
2713+
writeFieldName(p.currentName());
2714+
t = p.nextToken();
2715+
id = (t == null) ? ID_NOT_AVAILABLE : t.id();
2716+
// fall-through to copy the associated value
2717+
}
2718+
switch (id) {
2719+
case ID_START_OBJECT:
2720+
writeStartObject();
2721+
_copyCurrentContentsExact(p);
2722+
return;
2723+
case ID_START_ARRAY:
2724+
writeStartArray();
2725+
_copyCurrentContentsExact(p);
2726+
return;
2727+
2728+
default:
2729+
copyCurrentEventExact(p);
2730+
}
2731+
}
2732+
26932733
// @since 2.10
26942734
protected void _copyCurrentContents(JsonParser p) throws IOException
26952735
{
@@ -2753,6 +2793,69 @@ protected void _copyCurrentContents(JsonParser p) throws IOException
27532793
}
27542794
}
27552795

2796+
// @since 2.21
2797+
protected void _copyCurrentContentsExact(JsonParser p) throws IOException
2798+
{
2799+
int depth = 1;
2800+
JsonToken t;
2801+
2802+
// Mostly copied from `copyCurrentEventExact()`, but with added nesting counts
2803+
while ((t = p.nextToken()) != null) {
2804+
switch (t.id()) {
2805+
case ID_FIELD_NAME:
2806+
writeFieldName(p.currentName());
2807+
break;
2808+
2809+
case ID_START_ARRAY:
2810+
writeStartArray();
2811+
++depth;
2812+
break;
2813+
2814+
case ID_START_OBJECT:
2815+
writeStartObject();
2816+
++depth;
2817+
break;
2818+
2819+
case ID_END_ARRAY:
2820+
writeEndArray();
2821+
if (--depth == 0) {
2822+
return;
2823+
}
2824+
break;
2825+
case ID_END_OBJECT:
2826+
writeEndObject();
2827+
if (--depth == 0) {
2828+
return;
2829+
}
2830+
break;
2831+
2832+
case ID_STRING:
2833+
_copyCurrentStringValue(p);
2834+
break;
2835+
case ID_NUMBER_INT:
2836+
_copyCurrentIntValue(p);
2837+
break;
2838+
case ID_NUMBER_FLOAT:
2839+
_copyCurrentFloatValueExact(p);
2840+
break;
2841+
case ID_TRUE:
2842+
writeBoolean(true);
2843+
break;
2844+
case ID_FALSE:
2845+
writeBoolean(false);
2846+
break;
2847+
case ID_NULL:
2848+
writeNull();
2849+
break;
2850+
case ID_EMBEDDED_OBJECT:
2851+
writeObject(p.getEmbeddedObject());
2852+
break;
2853+
default:
2854+
throw new IllegalStateException("Internal error: unknown current token, "+t);
2855+
}
2856+
}
2857+
}
2858+
27562859
/**
27572860
* Method for copying current {@link JsonToken#VALUE_NUMBER_FLOAT} value;
27582861
* overridable by format backend implementations.

src/test/java/com/fasterxml/jackson/core/write/GeneratorCopyTest.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,4 +100,39 @@ void copyObjectTokens()
100100
gen.close();
101101
assertEquals("{\"a\":1}", sw.toString());
102102
}
103+
104+
@Test
105+
void copyNumericTokensExactly()
106+
throws Exception
107+
{
108+
JsonFactory jf = JSON_F;
109+
final String DOC = a2q("{ 'a':0.123456789123456789123456789, 'b':[" +
110+
"{ 'c' : null, 'd' : 0.123456789123456789123456789 }] }");
111+
try(JsonParser jp = jf.createParser(new StringReader(DOC))) {
112+
StringWriter sw = new StringWriter();
113+
try (JsonGenerator gen = jf.createGenerator(sw)) {
114+
assertToken(JsonToken.START_OBJECT, jp.nextToken());
115+
gen.copyCurrentStructureExact(jp);
116+
// which will advance parser to matching end Object
117+
assertToken(JsonToken.END_OBJECT, jp.currentToken());
118+
}
119+
120+
assertEquals(
121+
a2q("{'a':0.123456789123456789123456789,'b':[" +
122+
"{'c':null,'d':0.123456789123456789123456789}]}"),
123+
sw.toString()
124+
);
125+
}
126+
127+
try(JsonParser jp = jf.createParser(new StringReader("0.123456789123456789123456789"))) {
128+
StringWriter sw = new StringWriter();
129+
try (JsonGenerator gen = jf.createGenerator(sw)) {
130+
assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
131+
gen.copyCurrentStructureExact(jp);
132+
assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.currentToken());
133+
}
134+
135+
assertEquals("0.123456789123456789123456789", sw.toString());
136+
}
137+
}
103138
}

0 commit comments

Comments
 (0)