1 package com.fasterxml.jackson.databind.jsonschema; 2 3 import java.math.BigDecimal; 4 import java.math.BigInteger; 5 import java.util.*; 6 import java.util.concurrent.atomic.AtomicReference; 7 8 import com.fasterxml.jackson.annotation.JsonCreator; 9 import com.fasterxml.jackson.annotation.JsonPropertyOrder; 10 import com.fasterxml.jackson.annotation.JsonValue; 11 import com.fasterxml.jackson.core.JsonParser.NumberType; 12 import com.fasterxml.jackson.databind.*; 13 import com.fasterxml.jackson.databind.jsonFormatVisitors.*; 14 import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; 15 import com.fasterxml.jackson.databind.ser.std.StdSerializer; 16 17 /** 18 * Basic tests to exercise low-level support added for JSON Schema module and 19 * other modules that use type introspection. 20 */ 21 public class NewSchemaTest extends BaseMapTest 22 { 23 enum TestEnum { 24 A, B, C; 25 26 @Override toString()27 public String toString() { 28 return "ToString:"+name(); 29 } 30 } 31 32 enum TestEnumWithJsonValue { 33 A, B, C; 34 35 @JsonValue forSerialize()36 public String forSerialize() { 37 return "value-"+name(); 38 } 39 } 40 41 // silly little class to exercise basic traversal 42 static class POJO { 43 public List<POJO> children; 44 public POJO[] childOrdering; 45 public Map<String, java.util.Date> times; 46 public Map<String,Integer> conversions; 47 48 public EnumMap<TestEnum,Double> weights; 49 } 50 51 static class POJOWithScalars { 52 public boolean boo; 53 public byte b; 54 public char c; 55 public short s; 56 public int i; 57 public long l; 58 public float f; 59 public double d; 60 61 public byte[] arrayBoo; 62 public byte[] arrayb; 63 public char[] arrayc; 64 public short[] arrays; 65 public int[] arrayi; 66 public long[] arrayl; 67 public float[] arrayf; 68 public double[] arrayd; 69 70 public Boolean Boo; 71 public Byte B; 72 public Character C; 73 public Short S; 74 public Integer I; 75 public Long L; 76 public Float F; 77 public Double D; 78 79 public TestEnum en; 80 public String str; 81 public String[] strs; 82 public java.util.Date date; 83 public java.util.Calendar calendar; 84 } 85 86 static class POJOWithRefs { 87 public AtomicReference<POJO> maybePOJO; 88 89 public AtomicReference<String> maybeString; 90 } 91 92 // [databind#1793] 93 static class POJOWithJsonValue { 94 private Point[] value; 95 96 @JsonCreator(mode=JsonCreator.Mode.DELEGATING) POJOWithJsonValue(Point[] v)97 public POJOWithJsonValue(Point[] v) { value = v; } 98 99 @JsonValue serialization()100 public Point[] serialization() { return value; } 101 } 102 103 @JsonPropertyOrder({ "dec", "bigInt" }) 104 static class Numbers { 105 public BigDecimal dec; 106 public BigInteger bigInt; 107 } 108 109 static class BogusJsonFormatVisitorWrapper 110 extends JsonFormatVisitorWrapper.Base 111 { 112 // Implement handlers just to get more exercise... 113 114 @Override expectObjectFormat(JavaType type)115 public JsonObjectFormatVisitor expectObjectFormat(JavaType type) { 116 return new JsonObjectFormatVisitor.Base(getProvider()) { 117 @Override 118 public void property(BeanProperty prop) throws JsonMappingException { 119 _visit(prop); 120 } 121 122 @Override 123 public void property(String name, JsonFormatVisitable handler, 124 JavaType propertyTypeHint) { } 125 126 @Override 127 public void optionalProperty(BeanProperty prop) throws JsonMappingException { 128 _visit(prop); 129 } 130 131 @Override 132 public void optionalProperty(String name, JsonFormatVisitable handler, 133 JavaType propertyTypeHint) throws JsonMappingException { } 134 135 private void _visit(BeanProperty prop) throws JsonMappingException 136 { 137 if (!(prop instanceof BeanPropertyWriter)) { 138 return; 139 } 140 BeanPropertyWriter bpw = (BeanPropertyWriter) prop; 141 JsonSerializer<?> ser = bpw.getSerializer(); 142 final SerializerProvider prov = getProvider(); 143 if (ser == null) { 144 if (prov == null) { 145 throw new Error("SerializerProvider missing"); 146 } 147 ser = prov.findValueSerializer(prop.getType(), prop); 148 } 149 // and this just for bit of extra coverage... 150 if (ser instanceof StdSerializer) { 151 assertNotNull(((StdSerializer<?>) ser).getSchema(prov, prop.getType())); 152 } 153 JsonFormatVisitorWrapper visitor = new JsonFormatVisitorWrapper.Base(getProvider()); 154 ser.acceptJsonFormatVisitor(visitor, prop.getType()); 155 } 156 }; 157 } 158 159 @Override expectArrayFormat(JavaType type)160 public JsonArrayFormatVisitor expectArrayFormat(JavaType type) { 161 return new JsonArrayFormatVisitor.Base(getProvider()); 162 } 163 164 @Override expectStringFormat(JavaType type)165 public JsonStringFormatVisitor expectStringFormat(JavaType type) { 166 return new JsonStringFormatVisitor.Base(); 167 } 168 169 @Override expectNumberFormat(JavaType type)170 public JsonNumberFormatVisitor expectNumberFormat(JavaType type) { 171 return new JsonNumberFormatVisitor.Base(); 172 } 173 174 @Override expectIntegerFormat(JavaType type)175 public JsonIntegerFormatVisitor expectIntegerFormat(JavaType type) { 176 return new JsonIntegerFormatVisitor.Base(); 177 } 178 179 @Override expectBooleanFormat(JavaType type)180 public JsonBooleanFormatVisitor expectBooleanFormat(JavaType type) { 181 return new JsonBooleanFormatVisitor.Base(); 182 } 183 184 @Override expectNullFormat(JavaType type)185 public JsonNullFormatVisitor expectNullFormat(JavaType type) { 186 return new JsonNullFormatVisitor.Base(); 187 } 188 189 @Override expectAnyFormat(JavaType type)190 public JsonAnyFormatVisitor expectAnyFormat(JavaType type) { 191 return new JsonAnyFormatVisitor.Base(); 192 } 193 194 @Override expectMapFormat(JavaType type)195 public JsonMapFormatVisitor expectMapFormat(JavaType type) { 196 return new JsonMapFormatVisitor.Base(); 197 } 198 } 199 200 /* 201 /********************************************************** 202 /* Test methods 203 /********************************************************** 204 */ 205 206 private final ObjectMapper MAPPER = newJsonMapper(); 207 208 /* Silly little test for simply triggering traversal, without attempting to 209 * verify what is being reported. Smoke test that should trigger problems 210 * if basic POJO type/serializer traversal had issues. 211 */ 212 public void testBasicTraversal() throws Exception 213 { 214 MAPPER.acceptJsonFormatVisitor(POJO.class, new BogusJsonFormatVisitorWrapper()); 215 MAPPER.acceptJsonFormatVisitor(POJOWithScalars.class, new BogusJsonFormatVisitorWrapper()); 216 MAPPER.acceptJsonFormatVisitor(LinkedHashMap.class, new BogusJsonFormatVisitorWrapper()); 217 MAPPER.acceptJsonFormatVisitor(ArrayList.class, new BogusJsonFormatVisitorWrapper()); 218 MAPPER.acceptJsonFormatVisitor(EnumSet.class, new BogusJsonFormatVisitorWrapper()); 219 220 MAPPER.acceptJsonFormatVisitor(POJOWithRefs.class, new BogusJsonFormatVisitorWrapper()); 221 222 MAPPER.acceptJsonFormatVisitor(POJOWithJsonValue.class, new BogusJsonFormatVisitorWrapper()); 223 } 224 225 public void testSimpleEnum() throws Exception 226 { 227 final Set<String> values = new TreeSet<String>(); 228 ObjectWriter w = MAPPER.writer(SerializationFeature.WRITE_ENUMS_USING_TO_STRING); 229 230 w.acceptJsonFormatVisitor(TestEnum.class, new JsonFormatVisitorWrapper.Base() { 231 @Override 232 public JsonStringFormatVisitor expectStringFormat(JavaType type) { 233 return new JsonStringFormatVisitor() { 234 @Override 235 public void enumTypes(Set<String> enums) { 236 values.addAll(enums); 237 } 238 239 @Override 240 public void format(JsonValueFormat format) { } 241 }; 242 } 243 }); 244 245 assertEquals(3, values.size()); 246 TreeSet<String> exp = new TreeSet<String>(Arrays.asList( 247 "ToString:A", 248 "ToString:B", 249 "ToString:C" 250 )); 251 assertEquals(exp, values); 252 } 253 254 public void testEnumWithJsonValue() throws Exception 255 { 256 final Set<String> values = new TreeSet<String>(); 257 MAPPER.acceptJsonFormatVisitor(TestEnumWithJsonValue.class, 258 new JsonFormatVisitorWrapper.Base() { 259 @Override 260 public JsonStringFormatVisitor expectStringFormat(JavaType type) { 261 return new JsonStringFormatVisitor() { 262 @Override 263 public void enumTypes(Set<String> enums) { 264 values.addAll(enums); 265 } 266 267 @Override 268 public void format(JsonValueFormat format) { } 269 }; 270 } 271 }); 272 273 assertEquals(3, values.size()); 274 TreeSet<String> exp = new TreeSet<String>(Arrays.asList( 275 "value-A", 276 "value-B", 277 "value-C" 278 )); 279 assertEquals(exp, values); 280 } 281 282 // Ensure JsonValueFormat serializes/deserializes as expected 283 public void testJsonValueFormatHandling() throws Exception 284 { 285 // first: serialize using 'toString()', not name 286 final String EXP = quote("host-name"); 287 assertEquals(EXP, MAPPER.writeValueAsString(JsonValueFormat.HOST_NAME)); 288 289 // and second, deserialize ok from that as well 290 assertSame(JsonValueFormat.HOST_NAME, MAPPER.readValue(EXP, JsonValueFormat.class)); 291 } 292 293 // [databind#1045], regression wrt BigDecimal 294 public void testSimpleNumbers() throws Exception 295 { 296 final StringBuilder sb = new StringBuilder(); 297 298 MAPPER.acceptJsonFormatVisitor(Numbers.class, 299 new JsonFormatVisitorWrapper.Base() { 300 @Override 301 public JsonObjectFormatVisitor expectObjectFormat(final JavaType type) { 302 return new JsonObjectFormatVisitor.Base(getProvider()) { 303 @Override 304 public void optionalProperty(BeanProperty prop) throws JsonMappingException { 305 sb.append("[optProp ").append(prop.getName()).append("("); 306 JsonSerializer<Object> ser = null; 307 if (prop instanceof BeanPropertyWriter) { 308 BeanPropertyWriter bpw = (BeanPropertyWriter) prop; 309 ser = bpw.getSerializer(); 310 } 311 final SerializerProvider prov = getProvider(); 312 if (ser == null) { 313 ser = prov.findValueSerializer(prop.getType(), prop); 314 } 315 ser.acceptJsonFormatVisitor(new JsonFormatVisitorWrapper.Base() { 316 @Override 317 public JsonNumberFormatVisitor expectNumberFormat( 318 JavaType t) throws JsonMappingException { 319 return new JsonNumberFormatVisitor() { 320 @Override 321 public void format(JsonValueFormat format) { 322 sb.append("[numberFormat=").append(format).append("]"); 323 } 324 325 @Override 326 public void enumTypes(Set<String> enums) { } 327 328 @Override 329 public void numberType(NumberType numberType) { 330 sb.append("[numberType=").append(numberType).append("]"); 331 } 332 }; 333 } 334 335 @Override 336 public JsonIntegerFormatVisitor expectIntegerFormat(JavaType t) throws JsonMappingException { 337 return new JsonIntegerFormatVisitor() { 338 @Override 339 public void format(JsonValueFormat format) { 340 sb.append("[integerFormat=").append(format).append("]"); 341 } 342 343 @Override 344 public void enumTypes(Set<String> enums) { } 345 346 @Override 347 public void numberType(NumberType numberType) { 348 sb.append("[numberType=").append(numberType).append("]"); 349 } 350 }; 351 } 352 }, prop.getType()); 353 354 sb.append(")]"); 355 } 356 }; 357 } 358 }); 359 assertEquals("[optProp dec([numberType=BIG_DECIMAL])][optProp bigInt([numberType=BIG_INTEGER])]", 360 sb.toString()); 361 } 362 } 363