6565 * shared configuration can be created, via constructors.
6666 *
6767 * @author Victor Cordis ( cordis.victor at gmail.com)
68- * @version 1.7.2
68+ * @version 1.7.3
6969 * @see XMLWriter
7070 * @since 1.0
7171 */
@@ -366,7 +366,14 @@ public final Object readArray(Class componentType) {
366366 }
367367
368368 /**
369- * Consumes the XML until the entire subgraph is read.
369+ * Consumes the current XML element, stopping {@linkplain #atElementEnd()}.
370+ * Must be {@linkplain #atElementStart()}.
371+ */
372+ @ Override
373+ public abstract void consume ();
374+
375+ /**
376+ * Consumes the XML until the entire content is read.
370377 */
371378 protected abstract void consumeFully ();
372379
@@ -407,10 +414,11 @@ interface AliasingReflectionCacheSupplier {
407414 private boolean sharedConfiguration ;
408415 private UnmarshalContextImpl context ;
409416 /* default*/ Map <String , Object > cachedAliasingReflection ;
417+ private Set <String > maybeExclusions ;
410418 private Map <String , SimpleStrategy > simpleStrategies ;
411419 private Map <String , CompositeStrategy > compositeStrategies ;
412420 /* default*/ SimpleDateFormat dateFormat ;
413- /* default*/ SecurityPolicy securityPolicy ;
421+ /* default*/ SecurityPolicy maybeSecurityPolicy ;
414422
415423 /**
416424 * Creates a new configuration prototype instance.
@@ -428,10 +436,11 @@ private void init(AliasingReflectionCacheSupplier getAliasingReflectionCache) {
428436 this .sharedConfiguration = false ;
429437 this .context = new UnmarshalContextImpl ();
430438 this .cachedAliasingReflection = getAliasingReflectionCache .get ();
439+ this .maybeExclusions = null ; // lazy.
431440 this .simpleStrategies = new StrategyHashMap <>();
432441 this .compositeStrategies = new StrategyHashMap <>();
433442 this .dateFormat = new SimpleDateFormat (DTD .FORMAT_DATE );
434- this .securityPolicy = null ; // lazy.
443+ this .maybeSecurityPolicy = null ; // lazy.
435444 // add DTD strategies by default:
436445 this .simpleStrategies .put (DTD .TYPE_BASE64 , Base64Strategy .INSTANCE );
437446 this .simpleStrategies .put (DTD .TYPE_BOOLEAN , BooleanStrategy .INSTANCE );
@@ -463,10 +472,11 @@ private void initIdentically(XMLReader other) {
463472 this .sharedConfiguration = true ;
464473 this .context = new UnmarshalContextImpl ();
465474 this .cachedAliasingReflection = other .cachedAliasingReflection ;
475+ this .maybeExclusions = other .maybeExclusions ;
466476 this .simpleStrategies = other .simpleStrategies ;
467477 this .compositeStrategies = other .compositeStrategies ;
468478 this .dateFormat = new SimpleDateFormat (other .dateFormat .toPattern ());
469- this .securityPolicy = other .securityPolicy ;
479+ this .maybeSecurityPolicy = other .maybeSecurityPolicy ;
470480 }
471481
472482 /**
@@ -591,7 +601,7 @@ public Map<String, CompositeStrategy> getCompositeStrategies() {
591601 }
592602
593603 /**
594- * Gets the {@linkplain #securityPolicy} property, which is used to create,
604+ * Gets the SecurityPolicy property, which is used to create,
595605 * for security reasons, black- or whitelists of classes to be checked when
596606 * reading objects. If a security policy is defined and an illegal class is
597607 * found at read time then the read will halt and throw a
@@ -613,10 +623,10 @@ public Map<String, CompositeStrategy> getCompositeStrategies() {
613623 */
614624 public SecurityPolicy getSecurityPolicy () {
615625 this .checkNotSharedConfiguration ();
616- if (this .securityPolicy == null ) { // lazy:
617- this .securityPolicy = new SecurityPolicy ();
626+ if (this .maybeSecurityPolicy == null ) {
627+ this .maybeSecurityPolicy = new SecurityPolicy ();
618628 }
619- return this .securityPolicy ;
629+ return this .maybeSecurityPolicy ;
620630 }
621631
622632 /**
@@ -689,6 +699,21 @@ private static String qualifiedFieldKey(Class declaring, String field) {
689699 return declaring .getName () + FIELD_FQN_SEPARATOR + field ;
690700 }
691701
702+ /**
703+ * Excludes the given field, by name or alias.
704+ *
705+ * @param declaring class
706+ * @param field name or alias to exclude
707+ * @throws IllegalStateException if shared configuration
708+ */
709+ public void exclude (Class declaring , String field ) {
710+ this .checkNotSharedConfiguration ();
711+ if (maybeExclusions == null ) {
712+ maybeExclusions = new HashSet <>();
713+ }
714+ this .maybeExclusions .add (qualifiedFieldKey (declaring , field ));
715+ }
716+
692717 /**
693718 * Returns {@code true} if there is more to be read from the current input
694719 * or {@code false} if the document end tag was reached.
@@ -860,7 +885,7 @@ public Object readArray(Class componentType) {
860885 try {
861886 final Object ret = this .read0 (componentType );
862887 return ret ;
863- } catch (IllegalClassException | InvalidFormatException ex ) {
888+ } catch (RuntimeException ex ) {
864889 this .driver .consumeFully ();
865890 throw ex ;
866891 } finally {
@@ -954,20 +979,32 @@ private Object readObject()
954979 final String localPartName = this .driver .elementName ();
955980 // search the class for the specified property:
956981 Field f = null ;
982+ boolean skipF = false ;
957983 while (cls != Object .class ) {
984+
985+ if (this .context .excluded (cls , localPartName )) {
986+ skipF = true ;
987+ break ; // skip excluded field search.
988+ }
989+
958990 try {
959991 f = this .context .fieldFor (cls , localPartName );
960992 if (!Modifier .isStatic (f .getModifiers ())) {
961993 break ; // field found.
962994 }
963995 } catch (NoSuchFieldException searchInSuperclass ) {
964996 }
997+
965998 cls = cls .getSuperclass ();
966999 }
967- // check if field is indeed an instance property:
1000+ if (skipF ) {
1001+ this .driver .consume ();
1002+ continue ; // skip excluded field.
1003+ }
9681004 if (f == null ) {
9691005 throw new InvalidFormatException (this .driver .positionDescriptor (), "undefined property: " + cls .getName () + '.' + localPartName );
9701006 }
1007+ // check if field is indeed an instance property:
9711008 final ReflectionUtil .FieldInfo fi = ReflectionUtil .fieldInfoForWrite (f );
9721009 if (!fi .isProperty ) {
9731010 throw new InvalidFormatException (this .driver .positionDescriptor (), "not a property: " + cls .getName () + '.' + localPartName );
@@ -1023,8 +1060,8 @@ private Object readArray0(Class componentType) {
10231060 }
10241061
10251062 private void ensureSecurityPolicy (Object o ) {
1026- if (this .securityPolicy != null ) {
1027- this .securityPolicy .check (o .getClass ());
1063+ if (this .maybeSecurityPolicy != null ) {
1064+ this .maybeSecurityPolicy .check (o .getClass ());
10281065 }
10291066 }
10301067
@@ -1157,9 +1194,10 @@ public void close() {
11571194 this .decoded .clear ();
11581195 this .context = null ;
11591196 this .cachedAliasingReflection = null ;
1197+ this .maybeExclusions = null ;
11601198 this .compositeStrategies = null ;
11611199 this .simpleStrategies = null ;
1162- this .securityPolicy = null ;
1200+ this .maybeSecurityPolicy = null ;
11631201 }
11641202
11651203 /**
@@ -1287,6 +1325,11 @@ public Field fieldFor(Class declaring, String aliasOrName) throws NoSuchFieldExc
12871325 return ret ;
12881326 }
12891327
1328+ @ Override
1329+ public boolean excluded (Class declaring , String aliasOrName ) {
1330+ return maybeExclusions != null && maybeExclusions .contains (qualifiedFieldKey (declaring , aliasOrName ));
1331+ }
1332+
12901333 @ Override
12911334 public Date parseDate (String date ) throws ParseException {
12921335 return dateFormat .parse (date );
0 commit comments