Skip to content

Commit 1e919b3

Browse files
authored
Make JsonGenerator::writeTypePrefix method to not make a WRAPPER_ARRAY when typeIdDef.id == null (#1356)
1 parent bf78781 commit 1e919b3

3 files changed

Lines changed: 91 additions & 49 deletions

File tree

release-notes/CREDITS-2.x

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,3 +456,8 @@ Jared Stehler (@jaredstehler)
456456
Zhanghao (@zhangOranges)
457457
* Contributed #1305: Make helper methods of `WriterBasedJsonGenerator` non-final to allow overriding
458458
(2.18.0)
459+
460+
Eduard Gomoliako (@Gems)
461+
* Contributed #1356: Make `JsonGenerator::writeTypePrefix` method to not write a
462+
`WRAPPER_ARRAY` when `typeIdDef.id == null`
463+
(2.19.0)

release-notes/VERSION-2.x

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ a pure JSON library.
1717
2.19.0 (not yet released)
1818

1919
#1328: Optimize handling of `JsonPointer.head()`
20+
#1356: Make `JsonGenerator::writeTypePrefix` method to not write a
21+
`WRAPPER_ARRAY` when `typeIdDef.id == null`
22+
(contributed by Eduard G)
2023

2124
2.18.1 (28-Oct-2024)
2225

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

Lines changed: 83 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.util.concurrent.atomic.AtomicBoolean;
1111
import java.util.concurrent.atomic.AtomicInteger;
1212
import java.util.concurrent.atomic.AtomicLong;
13+
import java.util.Objects;
1314

1415
import com.fasterxml.jackson.core.JsonParser.NumberType;
1516
import com.fasterxml.jackson.core.exc.StreamWriteException;
@@ -1968,61 +1969,94 @@ public void writeTypeId(Object id) throws IOException {
19681969
*/
19691970
public WritableTypeId writeTypePrefix(WritableTypeId typeIdDef) throws IOException
19701971
{
1971-
Object id = typeIdDef.id;
1972-
1973-
final JsonToken valueShape = typeIdDef.valueShape;
1974-
if (canWriteTypeId()) {
1975-
typeIdDef.wrapperWritten = false;
1976-
// just rely on native type output method (sub-classes likely to override)
1977-
writeTypeId(id);
1978-
} else {
1979-
// No native type id; write wrappers
1980-
// Normally we only support String type ids (non-String reserved for native type ids)
1981-
String idStr = (id instanceof String) ? (String) id : String.valueOf(id);
1982-
typeIdDef.wrapperWritten = true;
1983-
1984-
Inclusion incl = typeIdDef.include;
1985-
// first: can not output "as property" if value not Object; if so, must do "as array"
1986-
if ((valueShape != JsonToken.START_OBJECT)
1987-
&& incl.requiresObjectContext()) {
1988-
typeIdDef.include = incl = WritableTypeId.Inclusion.WRAPPER_ARRAY;
1972+
// Are native type ids allowed? If so, use them; otherwise, use wrappers
1973+
final boolean wasStartObjectWritten = canWriteTypeId()
1974+
? _writeTypePrefixUsingNative(typeIdDef)
1975+
: _writeTypePrefixUsingWrapper(typeIdDef);
1976+
1977+
// And then possible start marker for value itself:
1978+
switch (typeIdDef.valueShape) {
1979+
case START_OBJECT:
1980+
if (!wasStartObjectWritten) {
1981+
writeStartObject(typeIdDef.forValue);
19891982
}
1983+
break;
1984+
case START_ARRAY:
1985+
writeStartArray(typeIdDef.forValue);
1986+
break;
1987+
default: // otherwise: no start marker
1988+
}
19901989

1991-
switch (incl) {
1992-
case PARENT_PROPERTY:
1993-
// nothing to do here, as it has to be written in suffix...
1994-
break;
1995-
case PAYLOAD_PROPERTY:
1996-
// only output as native type id; otherwise caller must handle using some
1997-
// other mechanism, so...
1998-
break;
1999-
case METADATA_PROPERTY:
2000-
// must have Object context by now, so simply write as field name
2001-
// Note, too, that it's bit tricky, since we must print START_OBJECT that is part
2002-
// of value first -- and then NOT output it later on: hence return "early"
2003-
writeStartObject(typeIdDef.forValue);
2004-
writeStringField(typeIdDef.asProperty, idStr);
2005-
return typeIdDef;
1990+
return typeIdDef;
1991+
}
20061992

2007-
case WRAPPER_OBJECT:
2008-
// NOTE: this is wrapper, not directly related to value to output, so don't pass
2009-
writeStartObject();
2010-
writeFieldName(idStr);
2011-
break;
2012-
case WRAPPER_ARRAY:
2013-
default: // should never occur but translate as "as-array"
2014-
writeStartArray(); // wrapper, not actual array object to write
2015-
writeString(idStr);
2016-
}
1993+
/**
1994+
* Writes a native type id (when supported by format).
1995+
*
1996+
* @return True if start of an object has been written, False otherwise.
1997+
*
1998+
* @since 2.19
1999+
*/
2000+
protected boolean _writeTypePrefixUsingNative(WritableTypeId typeIdDef) throws IOException {
2001+
typeIdDef.wrapperWritten = false;
2002+
writeTypeId(typeIdDef.id);
2003+
return false;
2004+
}
2005+
2006+
/**
2007+
* Writes a wrapper for the type id if necessary.
2008+
*
2009+
* @return True if start of an object has been written, false otherwise.
2010+
*
2011+
* @since 2.19
2012+
*/
2013+
protected boolean _writeTypePrefixUsingWrapper(WritableTypeId typeIdDef) throws IOException {
2014+
// Normally we only support String type ids (non-String reserved for native type ids)
2015+
final String id = Objects.toString(typeIdDef.id, null);
2016+
2017+
// If we don't have Type ID we don't write a wrapper.
2018+
if (id == null) {
2019+
return false;
20172020
}
2018-
// and finally possible start marker for value itself:
2019-
if (valueShape == JsonToken.START_OBJECT) {
2021+
2022+
Inclusion incl = typeIdDef.include;
2023+
2024+
// first: can not output "as property" if value not Object; if so, must do "as array"
2025+
if ((typeIdDef.valueShape != JsonToken.START_OBJECT) && incl.requiresObjectContext()) {
2026+
typeIdDef.include = incl = WritableTypeId.Inclusion.WRAPPER_ARRAY;
2027+
}
2028+
2029+
typeIdDef.wrapperWritten = true;
2030+
2031+
switch (incl) {
2032+
case PARENT_PROPERTY:
2033+
// nothing to do here, as it has to be written in suffix...
2034+
break;
2035+
case PAYLOAD_PROPERTY:
2036+
// only output as native type id; otherwise caller must handle using some
2037+
// other mechanism, so...
2038+
break;
2039+
case METADATA_PROPERTY:
2040+
// must have Object context by now, so simply write as field name
2041+
// Note, too, that it's bit tricky, since we must print START_OBJECT that is part
2042+
// of value first -- and then NOT output it later on: hence return "early"
20202043
writeStartObject(typeIdDef.forValue);
2021-
} else if (valueShape == JsonToken.START_ARRAY) {
2022-
// should we now set the current object?
2023-
writeStartArray();
2044+
writeStringField(typeIdDef.asProperty, id);
2045+
return true;
2046+
2047+
case WRAPPER_OBJECT:
2048+
// NOTE: this is wrapper, not directly related to value to output, so
2049+
// do NOT pass "typeIdDef.forValue"
2050+
writeStartObject();
2051+
writeFieldName(id);
2052+
break;
2053+
case WRAPPER_ARRAY:
2054+
default: // should never occur but translate as "as-array"
2055+
writeStartArray(); // wrapper, not actual array object to write
2056+
writeString(id);
20242057
}
2025-
return typeIdDef;
2058+
2059+
return false;
20262060
}
20272061

20282062
/**

0 commit comments

Comments
 (0)