11package com .fasterxml .jackson .databind .jsontype ;
22
3+ import java .util .ArrayList ;
34import java .util .List ;
45import java .util .Map ;
56
1314import com .fasterxml .jackson .databind .ObjectMapper ;
1415import com .fasterxml .jackson .databind .exc .InvalidDefinitionException ;
1516import com .fasterxml .jackson .databind .exc .InvalidTypeIdException ;
17+ import com .fasterxml .jackson .databind .exc .UnrecognizedPropertyException ;
1618import com .fasterxml .jackson .databind .json .JsonMapper ;
1719import com .fasterxml .jackson .databind .type .TypeFactory ;
1820
@@ -210,9 +212,33 @@ public void testAmbiguousClasses() throws Exception {
210212 }
211213 }
212214
215+ public void testUnrecognizedProperties () throws Exception {
216+ try {
217+ /*Cat cat =*/
218+ MAPPER .readValue (ambiguousCatJson , Cat .class );
219+ fail ("Unable to map, because there is unknown field 'age'" );
220+ } catch (UnrecognizedPropertyException e ) {
221+ verifyException (e , "Unrecognized field" );
222+ }
223+ }
224+
225+ public void testNotFailOnUnknownProperty () throws Exception {
226+ // Given:
227+ JsonMapper mapper = JsonMapper .builder ()
228+ .disable (DeserializationFeature .FAIL_ON_UNKNOWN_PROPERTIES )
229+ .build ();
230+ // When:
231+ Cat cat = mapper .readValue (ambiguousCatJson , Cat .class );
232+ // Then:
233+ // unknown proparty 'age' is ignored, and json is deserialized to Cat class
234+ assertTrue (cat instanceof Cat );
235+ assertSame (Cat .class , cat .getClass ());
236+ assertEquals ("Felix" , cat .name );
237+ }
238+
213239 public void testAmbiguousProperties () throws Exception {
214240 try {
215- /*Cat cat =*/ MAPPER .readValue (ambiguousCatJson , Cat .class );
241+ /*Feline cat =*/ MAPPER .readValue (ambiguousCatJson , Feline .class );
216242 fail ("Should not get here" );
217243 } catch (InvalidTypeIdException e ) {
218244 verifyException (e , "Cannot deduce unique subtype" );
@@ -225,7 +251,7 @@ public void testFailOnInvalidSubtype() throws Exception {
225251 .disable (DeserializationFeature .FAIL_ON_INVALID_SUBTYPE )
226252 .build ();
227253 // When:
228- Cat cat = mapper .readValue (ambiguousCatJson , Cat .class );
254+ Feline cat = mapper .readValue (ambiguousCatJson , Feline .class );
229255 // Then:
230256 assertNull (cat );
231257 }
@@ -269,4 +295,99 @@ public void testListSerialization() throws Exception {
269295 // Then:
270296 assertEquals (arrayOfCatsJson , json );
271297 }
298+
299+ @ JsonTypeInfo (use = DEDUCTION , defaultImpl = ListOfPlaces .class )
300+ @ JsonSubTypes ( {@ Type (ListOfPlaces .class ), @ Type (CompositePlace .class ), @ Type (Place .class )})
301+ interface WorthSeeing {}
302+
303+ public static class Place implements WorthSeeing {
304+ public String name ;
305+ }
306+
307+ public static class CompositePlace extends Place implements WorthSeeing {
308+
309+ public Map <String , WorthSeeing > places ;
310+ }
311+
312+ static class ListOfPlaces extends ArrayList <WorthSeeing > implements WorthSeeing {
313+ }
314+
315+ private static final String colosseumJson = a2q ("{'name': 'The Colosseum'}" );
316+ private static final String romanForumJson = a2q ("{'name': 'The Roman Forum'}" );
317+ private static final String romeJson = a2q ("{'name': 'Rome', 'places': {'colosseum': " + colosseumJson + "," + "'romanForum': " + romanForumJson +"}}" );
318+
319+ private static final String rialtoBridgeJson = a2q ("{'name': 'Rialto Bridge'}" );
320+ private static final String sighsBridgeJson = a2q ("{'name': 'The Bridge Of Sighs'}" );
321+ private static final String bridgesJson = a2q ("[" + rialtoBridgeJson +"," + sighsBridgeJson +"]" );
322+ private static final String veniceJson = a2q ("{'name': 'Venice', 'places': {'bridges': " + bridgesJson + "}}" );
323+
324+ private static final String alpsJson = a2q ("{'name': 'The Alps'}" );
325+ private static final String citesJson = a2q ("[" + romeJson + "," + veniceJson + "]" );
326+ private static final String italy = a2q ("{'name': 'Italy', 'places': {'mountains': " + alpsJson + ", 'cities': " + citesJson +"}}}" );
327+
328+ public void testSupertypeInferenceWhenDefaultDefined () throws Exception {
329+ //When:
330+ WorthSeeing worthSeeing = MAPPER .readValue (alpsJson , WorthSeeing .class );
331+ // Then:
332+ assertEqualsPlace ("The Alps" , worthSeeing );
333+ }
334+
335+ public void testDefaultImplementation () throws Exception {
336+ // When:
337+ WorthSeeing worthSeeing = MAPPER .readValue (citesJson , WorthSeeing .class );
338+ // Then:
339+ assertCities (worthSeeing );
340+ }
341+
342+ public void testCompositeInference () throws Exception {
343+ // When:
344+ WorthSeeing worthSeeing = MAPPER .readValue (italy , WorthSeeing .class );
345+ // Then:
346+ assertSame (CompositePlace .class , worthSeeing .getClass ());
347+ CompositePlace italy = (CompositePlace ) worthSeeing ;
348+ assertEquals ("Italy" , italy .name );
349+ assertEquals (2 , italy .places .size ());
350+ assertEqualsPlace ("The Alps" , italy .places .get ("mountains" ));
351+ assertEquals (2 , italy .places .size ());
352+ assertCities (italy .places .get ("cities" ));
353+ }
354+
355+ private void assertCities (WorthSeeing worthSeeing ) {
356+ assertSame (ListOfPlaces .class , worthSeeing .getClass ());
357+ ListOfPlaces cities = (ListOfPlaces ) worthSeeing ;
358+ assertEquals (2 , cities .size ());
359+ assertRome (cities .get (0 ));
360+ assertVenice (cities .get (1 ));
361+ }
362+
363+ private void assertRome (WorthSeeing worthSeeing ) {
364+ assertSame (CompositePlace .class , worthSeeing .getClass ());
365+ CompositePlace rome = (CompositePlace ) worthSeeing ;
366+ assertEquals ("Rome" , rome .name );
367+ assertEquals (2 , rome .places .size ());
368+ assertEqualsPlace ("The Colosseum" , rome .places .get ("colosseum" ));
369+ assertEqualsPlace ("The Roman Forum" , rome .places .get ("romanForum" ));
370+ }
371+
372+ private void assertVenice (WorthSeeing worthSeeing ) {
373+ assertSame (CompositePlace .class , worthSeeing .getClass ());
374+ CompositePlace venice = (CompositePlace ) worthSeeing ;
375+ assertEquals ("Venice" , venice .name );
376+ assertEquals (1 , venice .places .size ());
377+ assertVeniceBridges (venice .places .get ("bridges" ));
378+
379+ }
380+
381+ private void assertVeniceBridges (WorthSeeing worthSeeing ){
382+ assertSame (ListOfPlaces .class , worthSeeing .getClass ());
383+ ListOfPlaces bridges = (ListOfPlaces ) worthSeeing ;
384+ assertEqualsPlace ("Rialto Bridge" , bridges .get (0 ));
385+ assertEqualsPlace ("The Bridge Of Sighs" , bridges .get (1 ));
386+ }
387+
388+ private void assertEqualsPlace (String expectedName , WorthSeeing worthSeeing ){
389+ assertTrue (worthSeeing instanceof Place );
390+ assertSame (Place .class , worthSeeing .getClass ());
391+ assertEquals (expectedName ,((Place ) worthSeeing ).name );
392+ }
272393}
0 commit comments