I run into a strange JsonStreamContext hierarchy problem within my custom
deserializer when using JsonNode.traverse (TreeTraversingParser) as input
for my deserialization instead of deserializing an Inputstream. I tracked
down the problem to a simple example setup:
Input JSON:
{
"items": [
{
"name": "foo"
}
]
}
Defined by Container and Items Java classes:
static class Container {
Collection<Item> items;
public Collection<Item> getItems() {
return items;
}
public void setItems(Collection<Item> items) {
this.items = items;
}
}
static class Item {
@JsonDeserialize(using = ItemNameDeserializer.class)
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
The custom Deserializer on Item.name is only used to print out the
JsonStreamContext:
static class ItemNameDeserializer extends StdDeserializer<String> {
private static final long serialVersionUID = 1L;
public ItemNameDeserializer() {
super(String.class);
}
@Override
public String deserialize(JsonParser p, DeserializationContext ctxt) throws
IOException, JsonProcessingException {
debugOutContext(p.getParsingContext());
return p.readValueAs(String.class);
}
private void debugOutContext(JsonStreamContext ctx) {
System.out.println("Context: " + ctx.hashCode() + ",\tcurName=" +
ctx.getCurrentName() + ",\tcurIndex=" + ctx.getCurrentIndex() +",\ttype=" +
ctx.getTypeDesc() + ",\tcurValue=" + ctx.getCurrentValue());
if (ctx.getParent() != null) {
debugOutContext(ctx.getParent());
}
}
}
JSON input serialized and deserialized using default ObjectMapper
write/readValue methods:
ObjectMapper mapper = new ObjectMapper();
Container container = new Container();
Item item = new Item();
item.name = "foo";
container.items = new ArrayList<>();
container.items.add(item);
String json = mapper.writeValueAsString(container);
Container containerMashalled = mapper.readValue(json, Container.class);
Looking at the Context hierarchy, the output is as expected:
ROOT Context
OBJECT Context with value "Container " instance
ARRAY Context with Collection instance
OBJECT Context with "Item" instance
Context: 486898233, curName=name, curIndex=0, type=OBJECT,
curValue=ch.pbz.jackson.Test$Item@26be92ad
Context: 1282473384, curName=null, curIndex=0, type=ARRAY, curValue=[]
Context: 575593575, curName=items, curIndex=0, type=OBJECT,
curValue=ch.pbz.jackson.Test$Container@14acaea5
Context: 1188392295, curName=null, curIndex=0, type=ROOT, curValue=null
Now deserializing the same JSON using the JsonNode.traverse() as input:
ObjectMapper mapper = new ObjectMapper();
JsonParser jsonParser = mapper.getFactory().createParser(json);
JsonNode doc = jsonParser.readValueAsTree();
JsonParser traverse = doc.traverse(jsonParser.getCodec());
Container container = mapper.readValue(traverse, Container.class);
Looking at the Context hierarchy, the output is "strange"
- No ROOT Context ?
- OBJECT Context, but with Collection instance as current value instead of
"Container" instance??
ARRAY Context with no current value??
OBJECT Context with "Item" instance (correct!)
Context: 466505482, curName=name, curIndex=0, type=OBJECT,
curValue=ch.pbz.jackson.Test$Item@5e3a8624
Context: 1547425104, curName=null, curIndex=0, type=ARRAY, curValue=null
Context: 152134087, curName=items, curIndex=0, type=OBJECT, curValue=[]
It seems like the items OBJECT context is overridden with the current value
of the ARRAY context and the ARRAY context misses the correct current value.
Any suggestions if we use the ObjectMapper in an inappropriate way or if
this is a Jackson bug?
See attached Test.java complete test case to reproduce.
Regards,
Paolo
--
You received this message because you are subscribed to the Google Groups
"jackson-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
For more options, visit https://groups.google.com/d/optout.
package ch.pbz.jackson;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JsonStreamContext;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
public class Test {
static class ItemNameDeserializer extends StdDeserializer<String> {
private static final long serialVersionUID = 1L;
public ItemNameDeserializer() {
super(String.class);
}
@Override
public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
debugOutContext(p.getParsingContext());
return p.readValueAs(String.class);
}
@SuppressWarnings("deprecation")
private void debugOutContext(JsonStreamContext ctx) {
System.out.println("Context: " + ctx.hashCode() + ",\tcurName=" + ctx.getCurrentName() + ",\tcurIndex=" + ctx.getCurrentIndex() +",\ttype=" + ctx.getTypeDesc() + ",\tcurValue=" + ctx.getCurrentValue());
if (ctx.getParent() != null) {
debugOutContext(ctx.getParent());
}
}
}
static class Container {
Collection<Item> items;
public Collection<Item> getItems() {
return items;
}
public void setItems(Collection<Item> items) {
this.items = items;
}
}
static class Item {
@JsonDeserialize(using = ItemNameDeserializer.class)
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static void main(String... args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
Container container = new Container();
Item item = new Item();
item.name = "foo";
container.items = new ArrayList<>();
container.items.add(item);
String json = mapper.writeValueAsString(container);
System.out.println("JSON=" + json);
System.out.println("\nReading using mapper.readValue(json, Container.class)");
Container container2 = mapper.readValue(json, Container.class);
System.out.println(container2.items.iterator().next().getName());
System.out.println("\nReading using nmapper.readValue(traverse, Container.class)");
JsonParser jsonParser = mapper.getFactory().createParser(json);
JsonNode doc = jsonParser.readValueAsTree();
JsonParser traverse = doc.traverse(jsonParser.getCodec());
Container container3 = mapper.readValue(traverse, Container.class);
System.out.println(container3.items.iterator().next().getName());
}
}