package com.fasterxml.jackson.databind.creators; import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.databind.*; /** * Unit tests for verifying that it is possible to annotate * various kinds of things with {@link JsonCreator} annotation. */ public class TestPolymorphicCreators extends BaseMapTest { static class Animal { // All animals have names, for our demo purposes... public String name; protected Animal() { } /** * Creator method that can instantiate instances of * appropriate polymoprphic type */ @JsonCreator public static Animal create(@JsonProperty("type") String type) { if ("dog".equals(type)) { return new Dog(); } if ("cat".equals(type)) { return new Cat(); } throw new IllegalArgumentException("No such animal type ('"+type+"')"); } } static class Dog extends Animal { double barkVolume; // in decibels public Dog() { } public void setBarkVolume(double v) { barkVolume = v; } } static class Cat extends Animal { boolean likesCream; public int lives; public Cat() { } public void setLikesCream(boolean likesCreamSurely) { likesCream = likesCreamSurely; } } abstract static class AbstractRoot { protected final String opt; protected AbstractRoot(String opt) { this.opt = opt; } @JsonCreator public static final AbstractRoot make(@JsonProperty("which") int which, @JsonProperty("opt") String opt) { if (1 == which) { return new One(opt); } throw new RuntimeException("cannot instantiate " + which); } abstract public int getWhich(); public final String getOpt() { return opt; } } static final class One extends AbstractRoot { protected One(String opt) { super(opt); } @Override public int getWhich() { return 1; } } /* /********************************************************** /* Actual tests /********************************************************** */ private final ObjectMapper MAPPER = new ObjectMapper(); /** * Simple test to verify that it is possible to implement polymorphic * deserialization manually. */ public void testManualPolymorphicDog() throws Exception { // first, a dog, start with type Animal animal = MAPPER.readValue("{ \"type\":\"dog\", \"name\":\"Fido\", \"barkVolume\" : 95.0 }", Animal.class); assertEquals(Dog.class, animal.getClass()); assertEquals("Fido", animal.name); assertEquals(95.0, ((Dog) animal).barkVolume); } public void testManualPolymorphicCatBasic() throws Exception { // and finally, lactose-intolerant, but otherwise robust super-cat: Animal animal = MAPPER.readValue("{ \"name\" : \"Macavity\", \"type\":\"cat\", \"lives\":18, \"likesCream\":false }", Animal.class); assertEquals(Cat.class, animal.getClass()); assertEquals("Macavity", animal.name); // ... there's no one like Macavity! Cat cat = (Cat) animal; assertEquals(18, cat.lives); // ok, he can't drink dairy products. Let's verify: assertEquals(false, cat.likesCream); } public void testManualPolymorphicCatWithReorder() throws Exception { // Then cat; shuffle order to mandate buffering Animal animal = MAPPER.readValue("{ \"likesCream\":true, \"name\" : \"Venla\", \"type\":\"cat\" }", Animal.class); assertEquals(Cat.class, animal.getClass()); assertEquals("Venla", animal.name); // bah, of course cats like cream. But let's ensure Jackson won't mess with laws of nature! assertTrue(((Cat) animal).likesCream); } public void testManualPolymorphicWithNumbered() throws Exception { final ObjectWriter w = MAPPER.writerFor(AbstractRoot.class); final ObjectReader r = MAPPER.readerFor(AbstractRoot.class); AbstractRoot input = AbstractRoot.make(1, "oh hai!"); String json = w.writeValueAsString(input); AbstractRoot result = r.readValue(json); assertNotNull(result); assertEquals("oh hai!", result.getOpt()); } }