Skip to content

Commit 5fd8897

Browse files
author
Michael Riedel
committed
FasterXML#2208: Add tests for custom SubTypeValidators.
1 parent d57dedc commit 5fd8897

File tree

1 file changed

+277
-0
lines changed

1 file changed

+277
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
package com.fasterxml.jackson.databind.deser;
2+
3+
import java.util.List;
4+
5+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
6+
import com.fasterxml.jackson.databind.BaseMapTest;
7+
import com.fasterxml.jackson.databind.BeanDescription;
8+
import com.fasterxml.jackson.databind.DeserializationContext;
9+
import com.fasterxml.jackson.databind.JavaType;
10+
import com.fasterxml.jackson.databind.JsonMappingException;
11+
import com.fasterxml.jackson.databind.ObjectMapper;
12+
import com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig;
13+
import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
14+
import com.fasterxml.jackson.databind.json.JsonMapper;
15+
import com.fasterxml.jackson.databind.jsontype.impl.SubTypeValidator;
16+
17+
public class TestCustomSubTypeValidator
18+
extends BaseMapTest
19+
{
20+
/*
21+
/**********************************************************
22+
/* Some sample documents:
23+
/**********************************************************
24+
*/
25+
26+
protected static final String STRING_VALUE_JSON = "\"just a plain JSON string\"";
27+
28+
29+
protected static final String ZOO_JSON = "{"
30+
+ " \"name\" : \"Jacksonville Zoo\","
31+
+ " \"animals\": ["
32+
+ " { "
33+
+ " \"species\" : \"com.fasterxml.jackson.databind.deser.TestCustomSubTypeValidator$Warthog\","
34+
+ " \"name\" : \"Pumba\""
35+
+ " },"
36+
+ " { "
37+
+ " \"species\" : \"com.fasterxml.jackson.databind.deser.TestCustomSubTypeValidator$Lion\","
38+
+ " \"name\" : \"Kimba\""
39+
+ " },"
40+
+ " { "
41+
+ " \"species\" : \"com.fasterxml.jackson.databind.deser.TestCustomSubTypeValidator$Goat\","
42+
+ " \"name\" : \"Goaty McGoatface\""
43+
+ " },"
44+
+ " {"
45+
+ " \"species\" : \"com.fasterxml.jackson.databind.deser.TestCustomSubTypeValidator$Penguin\","
46+
+ " \"name\" : \"Tux\""
47+
+ " }"
48+
+ " ]"
49+
+ "}";
50+
51+
protected static final String PETTING_ZOO_JSON = "{"
52+
+ " \"name\" : \"Jacksonville Petting Zoo\","
53+
+ " \"animals\": ["
54+
+ " { "
55+
+ " \"species\" : \"com.fasterxml.jackson.databind.deser.TestCustomSubTypeValidator$Goat\","
56+
+ " \"name\" : \"Bill\""
57+
+ " },"
58+
+ " { "
59+
+ " \"species\" : \"com.fasterxml.jackson.databind.deser.TestCustomSubTypeValidator$Sheep\","
60+
+ " \"name\" : \"Shaun\""
61+
+ " }"
62+
+ " ]"
63+
+ "}";
64+
65+
/*
66+
/**********************************************************
67+
/* Helper classes (beans)
68+
/**********************************************************
69+
*/
70+
71+
public static class Zoo
72+
{
73+
private String _name;
74+
75+
private List<Animal> _animals;
76+
77+
public String getName()
78+
{
79+
return _name;
80+
}
81+
82+
public void setName(String name)
83+
{
84+
_name = name;
85+
}
86+
87+
public List<Animal> getAnimals()
88+
{
89+
return _animals;
90+
}
91+
92+
public void setAnimals(List<Animal> animals)
93+
{
94+
_animals = animals;
95+
}
96+
}
97+
98+
@JsonTypeInfo(
99+
use=JsonTypeInfo.Id.CLASS,
100+
include=JsonTypeInfo.As.PROPERTY,
101+
property="species"
102+
)
103+
public static abstract class Animal
104+
{
105+
private String _name;
106+
107+
public String getName() {
108+
return _name;
109+
}
110+
111+
public void setName(String name) {
112+
_name = name;
113+
}
114+
}
115+
116+
public static class Lion extends Animal
117+
{
118+
}
119+
120+
public static class Warthog extends Animal
121+
{
122+
}
123+
124+
public static class Goat extends Animal
125+
{
126+
}
127+
128+
public static class Sheep extends Animal
129+
{
130+
}
131+
132+
public static class Penguin extends Animal
133+
{
134+
}
135+
136+
/*
137+
/**********************************************************
138+
/* Helper classes (validators)
139+
/**********************************************************
140+
*/
141+
142+
static class DenyAllValidator
143+
extends SubTypeValidator
144+
{
145+
@Override
146+
public void validateSubType(DeserializationContext ctxt, JavaType type, BeanDescription beanDesc)
147+
throws JsonMappingException
148+
{
149+
ctxt.reportBadTypeDefinition(
150+
beanDesc,
151+
"Illegal type (%s) to deserialize: this validator does not allow deserialization at all!",
152+
type.getRawClass().getName());
153+
}
154+
}
155+
156+
static class FriendlyAnimalValidator
157+
extends SubTypeValidator
158+
{
159+
private static Class<?>[] FRIENDLY_ANIMAL_WHITELIST = new Class<?>[] {
160+
Animal.class,
161+
Goat.class,
162+
Sheep.class,
163+
Penguin.class
164+
};
165+
166+
@Override
167+
public void validateSubType(DeserializationContext ctxt, JavaType type, BeanDescription beanDesc)
168+
throws JsonMappingException
169+
{
170+
super.validateSubType(ctxt, type, beanDesc);
171+
172+
if (type.isTypeOrSubTypeOf(Animal.class)) {
173+
for (final Class<?> friendlyAnimalSpecies : FRIENDLY_ANIMAL_WHITELIST) {
174+
if (type.hasRawClass(friendlyAnimalSpecies)) {
175+
return;
176+
}
177+
}
178+
179+
ctxt.reportBadTypeDefinition(
180+
beanDesc,
181+
"Illegal type (%s) to deserialize: prevented because this animal species is not known to be friendly",
182+
type.getRawClass().getName());
183+
}
184+
}
185+
}
186+
187+
/*
188+
/**********************************************************
189+
/* Factory methods
190+
/**********************************************************
191+
*/
192+
193+
protected ObjectMapper objectMapper(SubTypeValidator validator)
194+
{
195+
final DeserializerFactoryConfig config =
196+
new DeserializerFactoryConfig()
197+
.withSubTypeValidator(validator);
198+
199+
final DeserializerFactory factory =
200+
new BeanDeserializerFactory(config);
201+
202+
final JsonMapper mapper =
203+
JsonMapper
204+
.builder()
205+
.deserializerFactory(factory)
206+
.build();
207+
208+
return mapper;
209+
}
210+
211+
/*
212+
/**********************************************************
213+
/* Unit tests
214+
/**********************************************************
215+
*/
216+
217+
public void testDefaultValidator() throws Exception
218+
{
219+
final ObjectMapper mapper = objectMapper();
220+
221+
mapper.readValue(STRING_VALUE_JSON, String.class);
222+
mapper.readValue(ZOO_JSON, Zoo.class);
223+
mapper.readValue(PETTING_ZOO_JSON, Zoo.class);
224+
}
225+
226+
public void testDenyAllValidator() throws Exception
227+
{
228+
final ObjectMapper mapper = objectMapper(new DenyAllValidator());
229+
230+
// still deserializes primitive JSON types
231+
mapper.readValue(STRING_VALUE_JSON, String.class);
232+
233+
// fails to deserialize, because the JSON contains objects that are mapped to beans
234+
while(true)
235+
{
236+
try {
237+
mapper.readValue(ZOO_JSON, Zoo.class);
238+
} catch(InvalidDefinitionException e) {
239+
break;
240+
}
241+
fail("Expected InvalidDefinitionException, but got none.");
242+
}
243+
244+
// fails to deserialize, because the JSON contains objects that are mapped to beans
245+
while(true)
246+
{
247+
try {
248+
mapper.readValue(PETTING_ZOO_JSON, Zoo.class);
249+
} catch(InvalidDefinitionException e) {
250+
break;
251+
}
252+
fail("Expected InvalidDefinitionException, but got none.");
253+
}
254+
}
255+
256+
public void testFriendlyAnimalValidator() throws Exception
257+
{
258+
final ObjectMapper mapper = objectMapper(new FriendlyAnimalValidator());
259+
260+
// still deserializes primitive JSON types
261+
mapper.readValue(STRING_VALUE_JSON, String.class);
262+
263+
// fails to deserialize, because the zoo has some unfriendly animals in it
264+
while(true)
265+
{
266+
try {
267+
mapper.readValue(ZOO_JSON, Zoo.class);
268+
} catch(InvalidDefinitionException e) {
269+
break;
270+
}
271+
fail("Expected InvalidDefinitionException, but got none.");
272+
}
273+
274+
// deserializes successfully, because the petting zoo has only friendly animals in it
275+
mapper.readValue(PETTING_ZOO_JSON, Zoo.class);
276+
}
277+
}

0 commit comments

Comments
 (0)