1

I'm trying to deserialize JSON data into Animal.class and Dog.class which extends Animal.class using custom deserializer. The JSON data below is a simplified one and the real JSON data cannot be deserialized with @JsonSubTypes.

This is how code looks:

  1. Given JSON data (I can't modify the JSON data format)
[
  {
    "name": "dog name",
    "type": "dog",
    "age": 5
  },
  {
    "name": "cat name",
    "type": "cat",
    "color": "black"
  }
]
  1. Classes
class Animal {
  String name;
}

@JsonDeserialize()
class Dog extends Animal {
    String name;
    int age;
}
  1. Custom deserializer : deserialize JSON data with "type": "dog" to Dog and all other animals to Animal.
public class AnimalDeserializer extends StdDeserializer<Animal> {
  @Override
  public Animal deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
    ObjectCodec codec = p.getCodec();
    JsonNode node = codec.readTree(p);

    JsonNode typeNode = node.get("type");
    if (typeNode != null && !typeNode.isNull()) {
      String typeText = typeNode.asText();

      if (typeText.equals("dog")) {
        return codec.treeToValue(node, Dog.class);
      }
    }

    return codec.treeToValue(node, Animal.class);
  }
}

It seems like return codec.treeToValue(node, Animal.class); is causing infinite loop. I solved the problem by making Animal.class abstract and creating a new class DefaultAnimal.class, but this way I have to make a lot of redundant default classes.

Please let me know if you have any good ideas.

1
  • As an idea: have a look at stackoverflow.com/questions/48619460/…. Maybe a BeanDeserializerModifier with a special handling for the Dog class is what you need Commented May 4 at 13:44

1 Answer 1

-1

You are right. return codec.treeToValue(node, Animal.class); is a guaranteed reason for the infinite loop. And even the call return codec.treeToValue(node, Dog.class); a few lines above might cause a infinite loop. Because you tell Jackson to deserialize the node into a Dog. Since Dog extends Animal and you did not provide a specific deserializer just for Dog Jackson might fall back to the AnimalDeserializer .

As you mentioned your use case is too complex to be be deserialized with @JsonSubTypes. The key to fixing the infinite loop is to avoid asking Jackson to deserialize the same base type using the same custom deserializer.

Maybe you have simplified your implementation for this question, too. In full implementation you would need not just handling for typeText.equals("dog") but for all cases. And you should take some fallback handling into account like returning null or returning some fallback object:

    @Override
    public Animal deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        ObjectCodec codec = p.getCodec();
        JsonNode node = codec.readTree(p);

        JsonNode typeNode = node.get("type");
        if (typeNode != null && !typeNode.isNull()) {
            String typeText = typeNode.asText();

            if (typeText.equals("dog")) {
                // this is risky, tried it with 2.18.3 and seems to work in this case
                return codec.treeToValue(node, Dog.class);
            }
        }

        // return codec.treeToValue(node, Animal.class);
        Animal fallback = new Animal();
        fallback.name = node.get("name").asText();
        fallback.type = node.get("type").asText() + " (fallback)";
        return fallback;
    }
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.