@@ -78,14 +78,63 @@ public static <Value, ElementValue> BiConsumer<Value, List<ElementValue>> fromLi
78
78
};
79
79
}
80
80
81
+ private interface UnknownFieldParser <Value , Context > {
82
+
83
+ void acceptUnknownField (String parserName , String field , XContentLocation location , XContentParser parser ,
84
+ Value value , Context context ) throws IOException ;
85
+ }
86
+
87
+ private static <Value , Context > UnknownFieldParser <Value , Context > ignoreUnknown () {
88
+ return (n , f , l , p , v , c ) -> p .skipChildren ();
89
+ }
90
+
91
+ private static <Value , Context > UnknownFieldParser <Value , Context > errorOnUnknown () {
92
+ return (n , f , l , p , v , c ) -> {
93
+ throw new XContentParseException (l , "[" + n + "] unknown field [" + f + "], parser not found" );
94
+ };
95
+ }
96
+
97
+ /**
98
+ * Defines how to consume a parsed undefined field
99
+ */
100
+ public interface UnknownFieldConsumer <Value > {
101
+ void accept (Value target , String field , Object value );
102
+ }
103
+
104
+ private static <Value , Context > UnknownFieldParser <Value , Context > consumeUnknownField (UnknownFieldConsumer <Value > consumer ) {
105
+ return (parserName , field , location , parser , value , context ) -> {
106
+ XContentParser .Token t = parser .currentToken ();
107
+ switch (t ) {
108
+ case VALUE_STRING :
109
+ consumer .accept (value , field , parser .text ());
110
+ break ;
111
+ case VALUE_NUMBER :
112
+ consumer .accept (value , field , parser .numberValue ());
113
+ break ;
114
+ case VALUE_BOOLEAN :
115
+ consumer .accept (value , field , parser .booleanValue ());
116
+ break ;
117
+ case VALUE_NULL :
118
+ consumer .accept (value , field , null );
119
+ break ;
120
+ case START_OBJECT :
121
+ consumer .accept (value , field , parser .map ());
122
+ break ;
123
+ case START_ARRAY :
124
+ consumer .accept (value , field , parser .list ());
125
+ break ;
126
+ default :
127
+ throw new XContentParseException (parser .getTokenLocation (),
128
+ "[" + parserName + "] cannot parse field [" + field + "] with value type [" + t + "]" );
129
+ }
130
+ };
131
+ }
132
+
81
133
private final Map <String , FieldParser > fieldParserMap = new HashMap <>();
82
134
private final String name ;
83
135
private final Supplier <Value > valueSupplier ;
84
- /**
85
- * Should this parser ignore unknown fields? This should generally be set to true only when parsing responses from external systems,
86
- * never when parsing requests from users.
87
- */
88
- private final boolean ignoreUnknownFields ;
136
+
137
+ private final UnknownFieldParser <Value , Context > unknownFieldParser ;
89
138
90
139
/**
91
140
* Creates a new ObjectParser instance with a name. This name is used to reference the parser in exceptions and messages.
@@ -95,25 +144,45 @@ public ObjectParser(String name) {
95
144
}
96
145
97
146
/**
98
- * Creates a new ObjectParser instance which a name.
147
+ * Creates a new ObjectParser instance with a name.
99
148
* @param name the parsers name, used to reference the parser in exceptions and messages.
100
149
* @param valueSupplier a supplier that creates a new Value instance used when the parser is used as an inner object parser.
101
150
*/
102
151
public ObjectParser (String name , @ Nullable Supplier <Value > valueSupplier ) {
103
- this (name , false , valueSupplier );
152
+ this (name , errorOnUnknown () , valueSupplier );
104
153
}
105
154
106
155
/**
107
- * Creates a new ObjectParser instance which a name.
156
+ * Creates a new ObjectParser instance with a name.
108
157
* @param name the parsers name, used to reference the parser in exceptions and messages.
109
158
* @param ignoreUnknownFields Should this parser ignore unknown fields? This should generally be set to true only when parsing
110
159
* responses from external systems, never when parsing requests from users.
111
160
* @param valueSupplier a supplier that creates a new Value instance used when the parser is used as an inner object parser.
112
161
*/
113
162
public ObjectParser (String name , boolean ignoreUnknownFields , @ Nullable Supplier <Value > valueSupplier ) {
163
+ this (name , ignoreUnknownFields ? ignoreUnknown () : errorOnUnknown (), valueSupplier );
164
+ }
165
+
166
+ /**
167
+ * Creates a new ObjectParser instance with a name.
168
+ * @param name the parsers name, used to reference the parser in exceptions and messages.
169
+ * @param unknownFieldConsumer how to consume parsed unknown fields
170
+ * @param valueSupplier a supplier that creates a new Value instance used when the parser is used as an inner object parser.
171
+ */
172
+ public ObjectParser (String name , UnknownFieldConsumer <Value > unknownFieldConsumer , @ Nullable Supplier <Value > valueSupplier ) {
173
+ this (name , consumeUnknownField (unknownFieldConsumer ), valueSupplier );
174
+ }
175
+
176
+ /**
177
+ * Creates a new ObjectParser instance with a name.
178
+ * @param name the parsers name, used to reference the parser in exceptions and messages.
179
+ * @param unknownFieldParser how to parse unknown fields
180
+ * @param valueSupplier a supplier that creates a new Value instance used when the parser is used as an inner object parser.
181
+ */
182
+ private ObjectParser (String name , UnknownFieldParser <Value , Context > unknownFieldParser , @ Nullable Supplier <Value > valueSupplier ) {
114
183
this .name = name ;
115
184
this .valueSupplier = valueSupplier ;
116
- this .ignoreUnknownFields = ignoreUnknownFields ;
185
+ this .unknownFieldParser = unknownFieldParser ;
117
186
}
118
187
119
188
/**
@@ -152,17 +221,18 @@ public Value parse(XContentParser parser, Value value, Context context) throws I
152
221
153
222
FieldParser fieldParser = null ;
154
223
String currentFieldName = null ;
224
+ XContentLocation currentPosition = null ;
155
225
while ((token = parser .nextToken ()) != XContentParser .Token .END_OBJECT ) {
156
226
if (token == XContentParser .Token .FIELD_NAME ) {
157
227
currentFieldName = parser .currentName ();
158
- fieldParser = getParser (currentFieldName , parser );
228
+ currentPosition = parser .getTokenLocation ();
229
+ fieldParser = fieldParserMap .get (currentFieldName );
159
230
} else {
160
231
if (currentFieldName == null ) {
161
232
throw new XContentParseException (parser .getTokenLocation (), "[" + name + "] no field found" );
162
233
}
163
234
if (fieldParser == null ) {
164
- assert ignoreUnknownFields : "this should only be possible if configured to ignore known fields" ;
165
- parser .skipChildren (); // noop if parser points to a value, skips children if parser is start object or start array
235
+ unknownFieldParser .acceptUnknownField (name , currentFieldName , currentPosition , parser , value , context );
166
236
} else {
167
237
fieldParser .assertSupports (name , parser , currentFieldName );
168
238
parseSub (parser , fieldParser , currentFieldName , value , context );
@@ -363,15 +433,6 @@ private void parseSub(XContentParser parser, FieldParser fieldParser, String cur
363
433
}
364
434
}
365
435
366
- private FieldParser getParser (String fieldName , XContentParser xContentParser ) {
367
- FieldParser parser = fieldParserMap .get (fieldName );
368
- if (parser == null && false == ignoreUnknownFields ) {
369
- throw new XContentParseException (xContentParser .getTokenLocation (),
370
- "[" + name + "] unknown field [" + fieldName + "], parser not found" );
371
- }
372
- return parser ;
373
- }
374
-
375
436
private class FieldParser {
376
437
private final Parser <Value , Context > parser ;
377
438
private final EnumSet <XContentParser .Token > supportedTokens ;
0 commit comments