diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x index 0faa36a5ba..caba22990f 100644 --- a/release-notes/CREDITS-2.x +++ b/release-notes/CREDITS-2.x @@ -975,3 +975,8 @@ Martín Coll (colltoaction@github) Andrey Kulikov (ankulikov@github) * Reported #2457: Extended enum values are not handled as enums when used as Map keys (2.10.1) + +Ryan Bohn (bohnman@github) + * Reported `StringCollectionSerializer` calls `JsonGenerator.setCurrentValue(value)`, + which messes up current value for sibling properties + (2.10.1) diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index f3582ea872..fd6afe21ea 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -8,6 +8,9 @@ Project: jackson-databind #2457: Extended enum values are not handled as enums when used as Map keys (reported by Andrey K) +#2475: `StringCollectionSerializer` calls `JsonGenerator.setCurrentValue(value)`, + which messes up current value for sibling properties + (reported by Ryan B) 2.10.0 (26-Sep-2019) diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/FromStringDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/FromStringDeserializer.java index 68187c1302..a640a539c3 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/FromStringDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/FromStringDeserializer.java @@ -24,7 +24,7 @@ * can be coerced into text, like Numbers and Booleans). * Simple JSON String values are trimmed using {@link java.lang.String#trim}. * Partial deserializer implementation will try to first access current token as - * a String, calls {@link #_deserialize(String,DeserializationContext)} and + * a String, calls {@code _deserialize(String,DeserializationContext)} and * returns return value. * If this does not work (current token not a simple scalar type), attempts * are made so that: diff --git a/src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java b/src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java index 6a4c4c4117..fdd47823ae 100644 --- a/src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java +++ b/src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java @@ -173,7 +173,7 @@ public void serialize(JsonGenerator f, SerializerProvider provider) throws IOExc { final List c = _children; final int size = c.size(); - f.writeStartArray(size); + f.writeStartArray(this, size); for (int i = 0; i < size; ++i) { // we'll typically have array list // For now, assuming it's either BaseJsonNode, JsonSerializable JsonNode n = c.get(i); diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/BeanAsArraySerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/BeanAsArraySerializer.java index 64ced81def..87d94b6a93 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/BeanAsArraySerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/BeanAsArraySerializer.java @@ -136,9 +136,9 @@ public void serializeWithType(Object bean, JsonGenerator gen, _serializeWithObjectId(bean, gen, provider, typeSer); return; } - gen.setCurrentValue(bean); WritableTypeId typeIdDef = _typeIdDef(typeSer, bean, JsonToken.START_ARRAY); typeSer.writeTypePrefix(gen, typeIdDef); + gen.setCurrentValue(bean); serializeAsArray(bean, gen, provider); typeSer.writeTypeSuffix(gen, typeIdDef); } @@ -161,9 +161,7 @@ && hasSingleElement(provider)) { * any getter, filtering) have already been checked; so code here * is trivial. */ - gen.writeStartArray(); - // [databind#631]: Assign current value, to be accessible by custom serializers - gen.setCurrentValue(bean); + gen.writeStartArray(bean); serializeAsArray(bean, gen, provider); gen.writeEndArray(); } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/IndexedListSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/IndexedListSerializer.java index 44f5c877a2..f42e78e155 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/IndexedListSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/IndexedListSerializer.java @@ -75,7 +75,7 @@ public final void serialize(List value, JsonGenerator gen, SerializerProvider return; } } - gen.writeStartArray(len); + gen.writeStartArray(value, len); serializeContents(value, gen, provider); gen.writeEndArray(); } @@ -182,7 +182,6 @@ public void serializeTypedContents(List value, JsonGenerator jgen, Serializer } } } catch (Exception e) { - // [JACKSON-55] Need to add reference information wrapAndThrow(provider, e, value, i); } } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/IndexedStringListSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/IndexedStringListSerializer.java index fbe88a6b57..ea817e7fa2 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/IndexedStringListSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/IndexedStringListSerializer.java @@ -73,7 +73,7 @@ public void serialize(List value, JsonGenerator g, return; } } - g.writeStartArray(len); + g.writeStartArray(value, len); serializeContents(value, g, provider, len); g.writeEndArray(); } @@ -85,6 +85,7 @@ public void serializeWithType(List value, JsonGenerator g, SerializerPro { WritableTypeId typeIdDef = typeSer.writeTypePrefix(g, typeSer.typeId(value, JsonToken.START_ARRAY)); + g.setCurrentValue(value); serializeContents(value, g, provider, value.size()); typeSer.writeTypeSuffix(g, typeIdDef); } @@ -92,7 +93,6 @@ public void serializeWithType(List value, JsonGenerator g, SerializerPro private final void serializeContents(List value, JsonGenerator g, SerializerProvider provider, int len) throws IOException { - g.setCurrentValue(value); int i = 0; try { for (; i < len; ++i) { diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/IteratorSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/IteratorSerializer.java index cf756db2bf..77846fc025 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/IteratorSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/IteratorSerializer.java @@ -63,7 +63,7 @@ public final void serialize(Iterator value, JsonGenerator gen, } } */ - gen.writeStartArray(); + gen.writeStartArray(value); serializeContents(value, gen, provider); gen.writeEndArray(); } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringArraySerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringArraySerializer.java index 1011babe52..94c83cfd74 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringArraySerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringArraySerializer.java @@ -167,7 +167,7 @@ public final void serialize(String[] value, JsonGenerator gen, SerializerProvide return; } } - gen.writeStartArray(len); + gen.writeStartArray(value, len); serializeContents(value, gen, provider); gen.writeEndArray(); } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringCollectionSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringCollectionSerializer.java index b936eac2a6..37ddd1c660 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringCollectionSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringCollectionSerializer.java @@ -67,7 +67,6 @@ protected void acceptContentVisitor(JsonArrayFormatVisitor visitor) throws JsonM public void serialize(Collection value, JsonGenerator g, SerializerProvider provider) throws IOException { - g.setCurrentValue(value); final int len = value.size(); if (len == 1) { if (((_unwrapSingle == null) && @@ -77,7 +76,7 @@ public void serialize(Collection value, JsonGenerator g, return; } } - g.writeStartArray(len); + g.writeStartArray(value, len); serializeContents(value, g, provider); g.writeEndArray(); } @@ -87,9 +86,9 @@ public void serializeWithType(Collection value, JsonGenerator g, SerializerProvider provider, TypeSerializer typeSer) throws IOException { - g.setCurrentValue(value); WritableTypeId typeIdDef = typeSer.writeTypePrefix(g, typeSer.typeId(value, JsonToken.START_ARRAY)); + g.setCurrentValue(value); serializeContents(value, g, provider); typeSer.writeTypeSuffix(g, typeIdDef); } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnknownSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnknownSerializer.java index 70b53e0634..738c55dd2e 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnknownSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnknownSerializer.java @@ -33,7 +33,7 @@ public void serialize(Object value, JsonGenerator gen, SerializerProvider provid failForEmpty(provider, value); } // But if it's fine, we'll just output empty JSON Object: - gen.writeStartObject(); + gen.writeStartObject(value, 0); gen.writeEndObject(); } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/ArraySerializerBase.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/ArraySerializerBase.java index 69af378709..47ff35903b 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/ArraySerializerBase.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/ArraySerializerBase.java @@ -118,9 +118,7 @@ public void serialize(T value, JsonGenerator gen, SerializerProvider provider) t return; } } - gen.setCurrentValue(value); - gen.writeStartArray(); - // [databind#631]: Assign current value, to be accessible by custom serializers + gen.writeStartArray(value); serializeContents(value, gen, provider); gen.writeEndArray(); } @@ -130,10 +128,10 @@ public final void serializeWithType(T value, JsonGenerator g, SerializerProvider TypeSerializer typeSer) throws IOException { - // [databind#631]: Assign current value, to be accessible by custom serializers - g.setCurrentValue(value); WritableTypeId typeIdDef = typeSer.writeTypePrefix(g, typeSer.typeId(value, JsonToken.START_ARRAY)); + // [databind#631]: Assign current value, to be accessible by custom serializers + g.setCurrentValue(value); serializeContents(value, g, provider); typeSer.writeTypeSuffix(g, typeIdDef); } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/AsArraySerializerBase.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/AsArraySerializerBase.java index 87647ba974..712cc31452 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/AsArraySerializerBase.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/AsArraySerializerBase.java @@ -244,9 +244,7 @@ && hasSingleElement(value)) { serializeContents(value, gen, provider); return; } - gen.writeStartArray(); - // [databind#631]: Assign current value, to be accessible by custom serializers - gen.setCurrentValue(value); + gen.writeStartArray(value); serializeContents(value, gen, provider); gen.writeEndArray(); } @@ -255,10 +253,10 @@ && hasSingleElement(value)) { public void serializeWithType(T value, JsonGenerator g, SerializerProvider provider, TypeSerializer typeSer) throws IOException { - // [databind#631]: Assign current value, to be accessible by custom serializers - g.setCurrentValue(value); WritableTypeId typeIdDef = typeSer.writeTypePrefix(g, typeSer.typeId(value, JsonToken.START_ARRAY)); + // [databind#631]: Assign current value, to be accessible by custom serializers + g.setCurrentValue(value); serializeContents(value, g, provider); typeSer.writeTypeSuffix(g, typeIdDef); } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/CollectionSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/CollectionSerializer.java index 6dec6a7838..321e6b755a 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/CollectionSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/CollectionSerializer.java @@ -103,7 +103,7 @@ public final void serialize(Collection value, JsonGenerator g, SerializerProv return; } } - g.writeStartArray(len); + g.writeStartArray(value, len); serializeContents(value, g, provider); g.writeEndArray(); } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSetSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSetSerializer.java index 6211d71281..3706edc101 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSetSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSetSerializer.java @@ -60,7 +60,7 @@ public final void serialize(EnumSet> value, JsonGenerator gen, return; } } - gen.writeStartArray(len); + gen.writeStartArray(value, len); serializeContents(value, gen, provider); gen.writeEndArray(); } @@ -77,9 +77,8 @@ public void serializeContents(EnumSet> value, JsonGenerator ge */ for (Enum en : value) { if (enumSer == null) { - /* 12-Jan-2010, tatu: Since enums cannot be polymorphic, let's - * not bother with typed serializer variant here - */ + // 12-Jan-2010, tatu: Since enums cannot be polymorphic, let's + // not bother with typed serializer variant here enumSer = provider.findValueSerializer(en.getDeclaringClass(), _property); } enumSer.serialize(en, gen, provider); diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/IterableSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/IterableSerializer.java index 5cc5068452..8ced8ca477 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/IterableSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/IterableSerializer.java @@ -70,7 +70,7 @@ public final void serialize(Iterable value, JsonGenerator gen, return; } } - gen.writeStartArray(); + gen.writeStartArray(value); serializeContents(value, gen, provider); gen.writeEndArray(); } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/ObjectArraySerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/ObjectArraySerializer.java index a113ae2a91..4475fce57c 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/ObjectArraySerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/ObjectArraySerializer.java @@ -209,7 +209,7 @@ public final void serialize(Object[] value, JsonGenerator gen, SerializerProvide return; } } - gen.writeStartArray(len); + gen.writeStartArray(value, len); serializeContents(value, gen, provider); gen.writeEndArray(); } diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdArraySerializers.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdArraySerializers.java index eb05fa4b52..c5c1a4c74e 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdArraySerializers.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdArraySerializers.java @@ -141,8 +141,7 @@ public final void serialize(boolean[] value, JsonGenerator g, SerializerProvider serializeContents(value, g, provider); return; } - g.writeStartArray(len); - g.setCurrentValue(value); + g.writeStartArray(value, len); serializeContents(value, g, provider); g.writeEndArray(); } @@ -219,8 +218,7 @@ public final void serialize(short[] value, JsonGenerator g, SerializerProvider p serializeContents(value, g, provider); return; } - g.writeStartArray(len); - g.setCurrentValue(value); + g.writeStartArray(value, len); serializeContents(value, g, provider); g.writeEndArray(); } @@ -274,8 +272,7 @@ public void serialize(char[] value, JsonGenerator g, SerializerProvider provider { // [JACKSON-289] allows serializing as 'sparse' char array too: if (provider.isEnabled(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS)) { - g.writeStartArray(value.length); - g.setCurrentValue(value); + g.writeStartArray(value, value.length); _writeArrayContents(g, value); g.writeEndArray(); } else { @@ -389,7 +386,6 @@ public final void serialize(int[] value, JsonGenerator g, SerializerProvider pro return; } // 11-May-2016, tatu: As per [core#277] we have efficient `writeArray(...)` available - g.setCurrentValue(value); g.writeArray(value, 0, value.length); } @@ -462,7 +458,6 @@ public final void serialize(long[] value, JsonGenerator g, SerializerProvider pr return; } // 11-May-2016, tatu: As per [core#277] we have efficient `writeArray(...)` available - g.setCurrentValue(value); g.writeArray(value, 0, value.length); } @@ -539,8 +534,7 @@ public final void serialize(float[] value, JsonGenerator g, SerializerProvider p serializeContents(value, g, provider); return; } - g.writeStartArray(len); - g.setCurrentValue(value); + g.writeStartArray(value, len); serializeContents(value, g, provider); g.writeEndArray(); } @@ -626,7 +620,6 @@ public final void serialize(double[] value, JsonGenerator g, SerializerProvider serializeContents(value, g, provider); return; } - g.setCurrentValue(value); // 11-May-2016, tatu: As per [core#277] we have efficient `writeArray(...)` available g.writeArray(value, 0, value.length); } diff --git a/src/main/java/com/fasterxml/jackson/databind/util/TokenBuffer.java b/src/main/java/com/fasterxml/jackson/databind/util/TokenBuffer.java index 288a25a386..c0cb33dc4b 100644 --- a/src/main/java/com/fasterxml/jackson/databind/util/TokenBuffer.java +++ b/src/main/java/com/fasterxml/jackson/databind/util/TokenBuffer.java @@ -679,8 +679,20 @@ public final void writeStartArray(int size) throws IOException _writeContext = _writeContext.createChildArrayContext(); } - // // TODO: add 2 more `writeStartArray()` methods from 2.10 (in 2.11 or later) - + @Override // since 2.10.1 + public void writeStartArray(Object forValue) throws IOException { + _writeContext.writeValue(); + _appendStartMarker(JsonToken.START_ARRAY); + _writeContext = _writeContext.createChildArrayContext(); + } + + @Override // since 2.10.1 + public void writeStartArray(Object forValue, int size) throws IOException { + _writeContext.writeValue(); + _appendStartMarker(JsonToken.START_ARRAY); + _writeContext = _writeContext.createChildArrayContext(forValue); + } + @Override public final void writeEndArray() throws IOException { @@ -705,16 +717,19 @@ public void writeStartObject(Object forValue) throws IOException { _writeContext.writeValue(); _appendStartMarker(JsonToken.START_OBJECT); - // 15-Aug-2019, tatu: Matching method only added in 2.10, don't yet call - JsonWriteContext ctxt = _writeContext.createChildObjectContext(); + JsonWriteContext ctxt = _writeContext.createChildObjectContext(forValue); + _writeContext = ctxt; + } + + @Override // since 2.10.1 + public void writeStartObject(Object forValue, int size) throws IOException + { + _writeContext.writeValue(); + _appendStartMarker(JsonToken.START_OBJECT); + JsonWriteContext ctxt = _writeContext.createChildObjectContext(forValue); _writeContext = ctxt; - if (forValue != null) { - ctxt.setCurrentValue(forValue); - } } - // // TODO: add 1 more `writeStartObject()` methods from 2.10 (in 2.11 or later) - @Override public final void writeEndObject() throws IOException { diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestCustomSerializers.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestCustomSerializers.java index 1feeba634b..f8be935277 100644 --- a/src/test/java/com/fasterxml/jackson/databind/ser/TestCustomSerializers.java +++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestCustomSerializers.java @@ -8,12 +8,16 @@ import org.w3c.dom.Element; +import com.fasterxml.jackson.annotation.JsonFilter; import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.io.CharacterEscapes; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; +import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; import com.fasterxml.jackson.databind.ser.std.CollectionSerializer; import com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer; import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer; @@ -146,11 +150,46 @@ public StringListWrapper(String... values) { } } + // [databind#2475] + static class MyFilter2475 extends SimpleBeanPropertyFilter { + @Override + public void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer) throws Exception { + // Ensure that "current value" remains pojo + final JsonStreamContext ctx = jgen.getOutputContext(); + final Object curr = ctx.getCurrentValue(); + + if (!(curr instanceof Item2475)) { + throw new Error("Field '"+writer.getName()+"', context not that of `Item2475` instance"); + } + super.serializeAsField(pojo, jgen, provider, writer); + } + } + + @JsonFilter("myFilter") + @JsonPropertyOrder({ "id", "set" }) + public static class Item2475 { + private Collection set; + private String id; + + public Item2475(Collection set, String id) { + this.set = set; + this.id = id; + } + + public Collection getSet() { + return set; + } + + public String getId() { + return id; + } + } + /* /********************************************************** /* Unit tests /********************************************************** - */ + */ private final ObjectMapper MAPPER = new ObjectMapper(); @@ -251,4 +290,19 @@ public void testWithCustomElements() throws Exception Set set = new LinkedHashSet(Arrays.asList("foo", null)); assertEquals(aposToQuotes("['FOO',null]"), mapper.writeValueAsString(set)); } + + // [databind#2475] + public void testIssue2475() throws Exception { + SimpleFilterProvider provider = new SimpleFilterProvider().addFilter("myFilter", new MyFilter2475()); + ObjectWriter writer = MAPPER.writer(provider); + + // contents don't really matter that much as verification within filter but... let's + // check anyway + assertEquals(aposToQuotes("{'id':'ID-1','set':[]}"), + writer.writeValueAsString(new Item2475(new ArrayList(), "ID-1"))); + + assertEquals(aposToQuotes("{'id':'ID-2','set':[]}"), + writer.writeValueAsString(new Item2475(new HashSet(), "ID-2"))); + } + }