2626import org .jboss .jandex .TypeVariable ;
2727import org .jboss .jandex .VoidType ;
2828
29+ import com .fasterxml .jackson .annotation .JsonAnySetter ;
2930import com .fasterxml .jackson .core .JacksonException ;
3031import com .fasterxml .jackson .core .JsonParser ;
3132import com .fasterxml .jackson .core .ObjectCodec ;
@@ -259,9 +260,12 @@ protected boolean createSerializationMethod(ClassInfo classInfo, ClassCreator cl
259260 generateTranslatableFieldNamesStaticField (classCreator , translatableNames );
260261 reverseIndexHandle = buildReverseIndexHandle (deserialize , classCreator , strategyHandle );
261262 }
263+ ResultHandle activeViewHandle = deserialize .invokeVirtualMethod (
264+ ofMethod (DeserializationContext .class , "getActiveView" , Class .class ),
265+ deserialize .getMethodParam (1 ));
262266 DeserializationData deserData = new DeserializationData (classInfo , ctor , classCreator , deserialize ,
263267 getJsonNode (deserialize ), parseTypeParameters (classInfo , classCreator ), new HashSet <>(),
264- namingStrategy , strategyHandle , reverseIndexHandle );
268+ namingStrategy , strategyHandle , reverseIndexHandle , activeViewHandle );
265269
266270 ResultHandle deserializedHandle = ctor .parametersCount () == 0
267271 ? deserData .methodCreator .newInstance (MethodDescriptor .ofConstructor (deserData .classInfo .name ().toString ()))
@@ -365,7 +369,6 @@ private static ResultHandle resolveLookupName(BytecodeCreator bytecode, FieldSpe
365369 }
366370
367371 private boolean deserializeObjectFields (DeserializationData deserData , ResultHandle objHandle ) {
368-
369372 ResultHandle fieldsIterator = deserData .methodCreator
370373 .invokeVirtualMethod (ofMethod (JsonNode .class , "fields" , Iterator .class ), deserData .jsonNode );
371374 BytecodeCreator loopCreator = deserData .methodCreator .whileLoop (c -> iteratorHasNext (c , fieldsIterator )).block ();
@@ -381,6 +384,9 @@ private boolean deserializeObjectFields(DeserializationData deserData, ResultHan
381384 // save constructor field names before deserializeFields modifies the set
382385 Set <String > ctorFields = Set .copyOf (deserData .constructorFields );
383386
387+ Set <String > ignoredProperties = getIgnoredProperties (deserData .classInfo );
388+ deserData .constructorFields .addAll (ignoredProperties );
389+
384390 ResultHandle deserializationContext = deserData .methodCreator .getMethodParam (1 );
385391 boolean result = deserializeFields (deserData , deserializationContext , objHandle , fieldValue ,
386392 deserData .constructorFields , strSwitch );
@@ -391,28 +397,61 @@ private boolean deserializeObjectFields(DeserializationData deserData, ResultHan
391397 });
392398 }
393399
394- strSwitch .defaultCase (bytecode -> {
395- ResultHandle failOnUnknown = bytecode .invokeVirtualMethod (
396- ofMethod (DeserializationContext .class , "isEnabled" , boolean .class , DeserializationFeature .class ),
397- deserializationContext ,
398- bytecode .readStaticField (FieldDescriptor .of (DeserializationFeature .class ,
399- "FAIL_ON_UNKNOWN_PROPERTIES" , DeserializationFeature .class )));
400- BytecodeCreator trueBranch = bytecode .ifTrue (failOnUnknown ).trueBranch ();
401- ResultHandle message = trueBranch .invokeVirtualMethod (
402- ofMethod (String .class , "concat" , String .class , String .class ),
403- trueBranch .load ("Unrecognized field \" " ),
404- trueBranch .invokeVirtualMethod (
405- ofMethod (String .class , "concat" , String .class , String .class ),
406- trueBranch .checkCast (fieldName , String .class ),
407- trueBranch .load ("\" " )));
408- ResultHandle exception = trueBranch .newInstance (
409- MethodDescriptor .ofConstructor (JsonMappingException .class , String .class ), message );
410- trueBranch .throwException (exception );
411- });
412-
400+ MethodInfo anySetterMethod = findAnySetterMethod (deserData .classInfo );
401+ handleUnknownFields (deserData , ignoredProperties , ctorFields , strSwitch , deserializationContext , fieldName ,
402+ fieldValue , objHandle , anySetterMethod );
413403 return result ;
414404 }
415405
406+ private static void handleUnknownFields (DeserializationData deserData , Set <String > ignoredProperties ,
407+ Set <String > ctorFields , Switch .StringSwitch strSwitch , ResultHandle deserializationContext ,
408+ ResultHandle fieldName , ResultHandle fieldValue , ResultHandle objHandle , MethodInfo anySetterMethod ) {
409+ // add no-op cases for explicitly ignored properties
410+ for (String ignoredProp : ignoredProperties ) {
411+ if (!ctorFields .contains (ignoredProp )) {
412+ strSwitch .caseOf (ignoredProp , bytecode -> {
413+ });
414+ }
415+ }
416+
417+ if (anySetterMethod != null ) {
418+ strSwitch .defaultCase (bytecode -> {
419+ ResultHandle deserializedValue = bytecode .invokeVirtualMethod (
420+ ofMethod (DeserializationContext .class , "readTreeAsValue" , Object .class , JsonNode .class , Class .class ),
421+ deserializationContext , fieldValue ,
422+ bytecode .loadClass (anySetterMethod .parameterType (1 ).name ().toString ()));
423+ ResultHandle castedFieldName = bytecode .checkCast (fieldName , String .class );
424+ if (anySetterMethod .declaringClass ().isInterface ()) {
425+ bytecode .invokeInterfaceMethod (anySetterMethod , objHandle , castedFieldName , deserializedValue );
426+ } else {
427+ bytecode .invokeVirtualMethod (anySetterMethod , objHandle , castedFieldName , deserializedValue );
428+ }
429+ });
430+ } else if (shouldIgnoreUnknownProperties (deserData .classInfo )) {
431+ strSwitch .defaultCase (bytecode -> {
432+ });
433+ } else {
434+ strSwitch .defaultCase (bytecode -> {
435+ ResultHandle failOnUnknown = bytecode .invokeVirtualMethod (
436+ ofMethod (DeserializationContext .class , "isEnabled" , boolean .class , DeserializationFeature .class ),
437+ deserializationContext ,
438+ bytecode .readStaticField (FieldDescriptor .of (DeserializationFeature .class ,
439+ "FAIL_ON_UNKNOWN_PROPERTIES" , DeserializationFeature .class )));
440+ BytecodeCreator trueBranch = bytecode .ifTrue (failOnUnknown ).trueBranch ();
441+ ResultHandle message = trueBranch .invokeVirtualMethod (
442+ ofMethod (String .class , "concat" , String .class , String .class ),
443+ trueBranch .load ("Unrecognized field \" " ),
444+ trueBranch .invokeVirtualMethod (
445+ ofMethod (String .class , "concat" , String .class , String .class ),
446+ trueBranch .checkCast (fieldName , String .class ),
447+ trueBranch .load ("\" " )));
448+ ResultHandle exception = trueBranch .newInstance (
449+ MethodDescriptor .ofConstructor (JsonMappingException .class , String .class ), message );
450+ trueBranch .throwException (exception );
451+ });
452+ }
453+ }
454+
416455 /**
417456 * Generates bytecode that builds a {@code Map<String, String>} reverse-name index once,
418457 * before the field iteration loop. The map is {@code null} when no strategy is configured,
@@ -572,6 +611,8 @@ private void deserializeFieldSpecs(DeserializationData deserData, ResultHandle d
572611 private boolean deserializeField (DeserializationData deserData , BytecodeCreator bytecode ,
573612 ResultHandle objHandle , ResultHandle fieldValue , FieldSpecs fieldSpecs ,
574613 ResultHandle deserializationContext ) {
614+ bytecode = deserializeViewClasses (deserData , bytecode , fieldSpecs );
615+
575616 boolean isBasicType = JacksonSerializationUtils .isBasicJsonType (fieldSpecs .fieldType );
576617
577618 // For non-basic types (objects, collections, boxed primitives, etc.), wrap in try-catch
@@ -604,6 +645,34 @@ private boolean deserializeField(DeserializationData deserData, BytecodeCreator
604645 return true ;
605646 }
606647
648+ private static BytecodeCreator deserializeViewClasses (DeserializationData deserData , BytecodeCreator bytecode ,
649+ FieldSpecs fieldSpecs ) {
650+ String [] viewClasses = fieldSpecs .viewClasses ();
651+ if (viewClasses != null ) {
652+ ResultHandle viewClassesArray = bytecode .newArray (Class .class , viewClasses .length );
653+ for (int i = 0 ; i < viewClasses .length ; i ++) {
654+ bytecode .writeArrayValue (viewClassesArray , i , bytecode .loadClass (viewClasses [i ]));
655+ }
656+ MethodDescriptor isViewIncluded = ofMethod (JacksonMapperUtil .class , "isViewIncluded" ,
657+ boolean .class , Class .class , Class [].class );
658+ ResultHandle included = bytecode .invokeStaticMethod (isViewIncluded , deserData .activeViewHandle (),
659+ viewClassesArray );
660+ bytecode = bytecode .ifTrue (included ).trueBranch ();
661+ }
662+ return bytecode ;
663+ }
664+
665+ private MethodInfo findAnySetterMethod (ClassInfo classInfo ) {
666+ for (MethodInfo method : classMethods (classInfo )) {
667+ if (method .hasAnnotation (JsonAnySetter .class )
668+ && method .parametersCount () == 2
669+ && !Modifier .isStatic (method .flags ())) {
670+ return method ;
671+ }
672+ }
673+ return null ;
674+ }
675+
607676 private FieldSpecs fieldSpecsFromMethod (MethodInfo methodInfo , PropertyNamingStrategy namingStrategy ) {
608677 return isSetterMethod (methodInfo ) ? new FieldSpecs (null , null , methodInfo , namingStrategy ) : null ;
609678 }
@@ -761,6 +830,7 @@ protected boolean shouldGenerateCodeFor(ClassInfo classInfo) {
761830 private record DeserializationData (ClassInfo classInfo , MethodInfo constructor , ClassCreator classCreator ,
762831 MethodCreator methodCreator ,
763832 ResultHandle jsonNode , Map <String , Integer > typeParametersIndex , Set <String > constructorFields ,
764- PropertyNamingStrategy namingStrategy , ResultHandle strategyHandle , ResultHandle reverseIndexHandle ) {
833+ PropertyNamingStrategy namingStrategy , ResultHandle strategyHandle , ResultHandle reverseIndexHandle ,
834+ ResultHandle activeViewHandle ) {
765835 }
766836}
0 commit comments