Skip to content

Commit

Permalink
Make JsonGenerator::writeTypePrefix method to not make a `WRAPPER_A…
Browse files Browse the repository at this point in the history
…RRAY` when `typeIdDef.id == null` (#1356)
  • Loading branch information
Gems authored Nov 9, 2024
1 parent bf78781 commit 1e919b3
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 49 deletions.
5 changes: 5 additions & 0 deletions release-notes/CREDITS-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -456,3 +456,8 @@ Jared Stehler (@jaredstehler)
Zhanghao (@zhangOranges)
* Contributed #1305: Make helper methods of `WriterBasedJsonGenerator` non-final to allow overriding
(2.18.0)

Eduard Gomoliako (@Gems)
* Contributed #1356: Make `JsonGenerator::writeTypePrefix` method to not write a
`WRAPPER_ARRAY` when `typeIdDef.id == null`
(2.19.0)
3 changes: 3 additions & 0 deletions release-notes/VERSION-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ a pure JSON library.
2.19.0 (not yet released)

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

2.18.1 (28-Oct-2024)

Expand Down
132 changes: 83 additions & 49 deletions src/main/java/com/fasterxml/jackson/core/JsonGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.Objects;

import com.fasterxml.jackson.core.JsonParser.NumberType;
import com.fasterxml.jackson.core.exc.StreamWriteException;
Expand Down Expand Up @@ -1968,61 +1969,94 @@ public void writeTypeId(Object id) throws IOException {
*/
public WritableTypeId writeTypePrefix(WritableTypeId typeIdDef) throws IOException
{
Object id = typeIdDef.id;

final JsonToken valueShape = typeIdDef.valueShape;
if (canWriteTypeId()) {
typeIdDef.wrapperWritten = false;
// just rely on native type output method (sub-classes likely to override)
writeTypeId(id);
} else {
// No native type id; write wrappers
// Normally we only support String type ids (non-String reserved for native type ids)
String idStr = (id instanceof String) ? (String) id : String.valueOf(id);
typeIdDef.wrapperWritten = true;

Inclusion incl = typeIdDef.include;
// first: can not output "as property" if value not Object; if so, must do "as array"
if ((valueShape != JsonToken.START_OBJECT)
&& incl.requiresObjectContext()) {
typeIdDef.include = incl = WritableTypeId.Inclusion.WRAPPER_ARRAY;
// Are native type ids allowed? If so, use them; otherwise, use wrappers
final boolean wasStartObjectWritten = canWriteTypeId()
? _writeTypePrefixUsingNative(typeIdDef)
: _writeTypePrefixUsingWrapper(typeIdDef);

// And then possible start marker for value itself:
switch (typeIdDef.valueShape) {
case START_OBJECT:
if (!wasStartObjectWritten) {
writeStartObject(typeIdDef.forValue);
}
break;
case START_ARRAY:
writeStartArray(typeIdDef.forValue);
break;
default: // otherwise: no start marker
}

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

case WRAPPER_OBJECT:
// NOTE: this is wrapper, not directly related to value to output, so don't pass
writeStartObject();
writeFieldName(idStr);
break;
case WRAPPER_ARRAY:
default: // should never occur but translate as "as-array"
writeStartArray(); // wrapper, not actual array object to write
writeString(idStr);
}
/**
* Writes a native type id (when supported by format).
*
* @return True if start of an object has been written, False otherwise.
*
* @since 2.19
*/
protected boolean _writeTypePrefixUsingNative(WritableTypeId typeIdDef) throws IOException {
typeIdDef.wrapperWritten = false;
writeTypeId(typeIdDef.id);
return false;
}

/**
* Writes a wrapper for the type id if necessary.
*
* @return True if start of an object has been written, false otherwise.
*
* @since 2.19
*/
protected boolean _writeTypePrefixUsingWrapper(WritableTypeId typeIdDef) throws IOException {
// Normally we only support String type ids (non-String reserved for native type ids)
final String id = Objects.toString(typeIdDef.id, null);

// If we don't have Type ID we don't write a wrapper.
if (id == null) {
return false;
}
// and finally possible start marker for value itself:
if (valueShape == JsonToken.START_OBJECT) {

Inclusion incl = typeIdDef.include;

// first: can not output "as property" if value not Object; if so, must do "as array"
if ((typeIdDef.valueShape != JsonToken.START_OBJECT) && incl.requiresObjectContext()) {
typeIdDef.include = incl = WritableTypeId.Inclusion.WRAPPER_ARRAY;
}

typeIdDef.wrapperWritten = true;

switch (incl) {
case PARENT_PROPERTY:
// nothing to do here, as it has to be written in suffix...
break;
case PAYLOAD_PROPERTY:
// only output as native type id; otherwise caller must handle using some
// other mechanism, so...
break;
case METADATA_PROPERTY:
// must have Object context by now, so simply write as field name
// Note, too, that it's bit tricky, since we must print START_OBJECT that is part
// of value first -- and then NOT output it later on: hence return "early"
writeStartObject(typeIdDef.forValue);
} else if (valueShape == JsonToken.START_ARRAY) {
// should we now set the current object?
writeStartArray();
writeStringField(typeIdDef.asProperty, id);
return true;

case WRAPPER_OBJECT:
// NOTE: this is wrapper, not directly related to value to output, so
// do NOT pass "typeIdDef.forValue"
writeStartObject();
writeFieldName(id);
break;
case WRAPPER_ARRAY:
default: // should never occur but translate as "as-array"
writeStartArray(); // wrapper, not actual array object to write
writeString(id);
}
return typeIdDef;

return false;
}

/**
Expand Down

0 comments on commit 1e919b3

Please sign in to comment.