Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions src/main/java/com/fasterxml/jackson/core/JsonGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -2690,6 +2690,46 @@ public void copyCurrentStructure(JsonParser p) throws IOException
}
}

/**
* Same as {@link #copyCurrentStructure} with the exception that copying of numeric
* values tries to avoid any conversion losses; in particular for floating-point
* numbers. This usually matters when transcoding from textual format like JSON
* to a binary format.
* See {@link #_copyCurrentFloatValueExact} for details.
*
* @param p Parser that points to the value to copy
*
* @throws IOException if there is either an underlying I/O problem or encoding
* issue at format layer
*
* @since 2.21
*/
public void copyCurrentStructureExact(JsonParser p) throws IOException
{
JsonToken t = p.currentToken();
// Let's handle field-name separately first
int id = (t == null) ? ID_NOT_AVAILABLE : t.id();
if (id == ID_FIELD_NAME) {
writeFieldName(p.currentName());
t = p.nextToken();
id = (t == null) ? ID_NOT_AVAILABLE : t.id();
// fall-through to copy the associated value
}
switch (id) {
case ID_START_OBJECT:
writeStartObject();
_copyCurrentContentsExact(p);
return;
case ID_START_ARRAY:
writeStartArray();
_copyCurrentContentsExact(p);
return;

default:
copyCurrentEventExact(p);
}
}

// @since 2.10
protected void _copyCurrentContents(JsonParser p) throws IOException
{
Expand Down Expand Up @@ -2753,6 +2793,69 @@ protected void _copyCurrentContents(JsonParser p) throws IOException
}
}

// @since 2.21
protected void _copyCurrentContentsExact(JsonParser p) throws IOException
{
int depth = 1;
JsonToken t;

// Mostly copied from `copyCurrentEventExact()`, but with added nesting counts
while ((t = p.nextToken()) != null) {
switch (t.id()) {
case ID_FIELD_NAME:
writeFieldName(p.currentName());
break;

case ID_START_ARRAY:
writeStartArray();
++depth;
break;

case ID_START_OBJECT:
writeStartObject();
++depth;
break;

case ID_END_ARRAY:
writeEndArray();
if (--depth == 0) {
return;
}
break;
case ID_END_OBJECT:
writeEndObject();
if (--depth == 0) {
return;
}
break;

case ID_STRING:
_copyCurrentStringValue(p);
break;
case ID_NUMBER_INT:
_copyCurrentIntValue(p);
break;
case ID_NUMBER_FLOAT:
_copyCurrentFloatValueExact(p);
break;
case ID_TRUE:
writeBoolean(true);
break;
case ID_FALSE:
writeBoolean(false);
break;
case ID_NULL:
writeNull();
break;
case ID_EMBEDDED_OBJECT:
writeObject(p.getEmbeddedObject());
break;
default:
throw new IllegalStateException("Internal error: unknown current token, "+t);
}
}
}

/**
* Method for copying current {@link JsonToken#VALUE_NUMBER_FLOAT} value;
* overridable by format backend implementations.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,39 @@ void copyObjectTokens()
gen.close();
assertEquals("{\"a\":1}", sw.toString());
}

@Test
void copyNumericTokensExactly()
throws Exception
{
JsonFactory jf = JSON_F;
final String DOC = a2q("{ 'a':0.123456789123456789123456789, 'b':[" +
"{ 'c' : null, 'd' : 0.123456789123456789123456789 }] }");
try(JsonParser jp = jf.createParser(new StringReader(DOC))) {
StringWriter sw = new StringWriter();
try (JsonGenerator gen = jf.createGenerator(sw)) {
assertToken(JsonToken.START_OBJECT, jp.nextToken());
gen.copyCurrentStructureExact(jp);
// which will advance parser to matching end Object
assertToken(JsonToken.END_OBJECT, jp.currentToken());
}

assertEquals(
a2q("{'a':0.123456789123456789123456789,'b':[" +
"{'c':null,'d':0.123456789123456789123456789}]}"),
sw.toString()
);
}

try(JsonParser jp = jf.createParser(new StringReader("0.123456789123456789123456789"))) {
StringWriter sw = new StringWriter();
try (JsonGenerator gen = jf.createGenerator(sw)) {
assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
gen.copyCurrentStructureExact(jp);
assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.currentToken());
}

assertEquals("0.123456789123456789123456789", sw.toString());
}
}
}
Loading