Skip to content

Commit 8bed4d8

Browse files
authored
Allow TokenFilter to preserve empty (#729)
This creates two new method on `TokenFilter` which you can override to decide if empty arrays and objects should be included or excluded. Closes #715
1 parent e302dd8 commit 8bed4d8

File tree

6 files changed

+454
-5
lines changed

6 files changed

+454
-5
lines changed

src/main/java/com/fasterxml/jackson/core/filter/FilteringParserDelegate.java

+60-4
Original file line numberDiff line numberDiff line change
@@ -273,16 +273,22 @@ public JsonToken nextToken() throws IOException
273273
_exposedContext = null;
274274
if (ctxt.inArray()) {
275275
t = delegate.getCurrentToken();
276-
// Is this guaranteed to work without further checks?
277-
// if (t != JsonToken.START_ARRAY) {
278276
_currToken = t;
277+
if (_currToken == JsonToken.END_ARRAY) {
278+
_headContext = _headContext.getParent();
279+
_itemFilter = _headContext.getFilter();
280+
}
279281
return t;
280282
}
281283

282284
// 19-Jul-2021, tatu: [core#700]: following was commented out?!
283285
// Almost! Most likely still have the current token;
284286
// with the sole exception of FIELD_NAME
285287
t = delegate.currentToken();
288+
if (t == JsonToken.END_OBJECT) {
289+
_headContext = _headContext.getParent();
290+
_itemFilter = _headContext.getFilter();
291+
}
286292
if (t != JsonToken.FIELD_NAME) {
287293
_currToken = t;
288294
return t;
@@ -562,12 +568,15 @@ protected final JsonToken _nextToken2() throws IOException
562568
continue main_loop;
563569

564570
case ID_END_ARRAY:
565-
case ID_END_OBJECT:
566571
{
567572
boolean returnEnd = _headContext.isStartHandled();
568573
f = _headContext.getFilter();
569574
if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
575+
boolean includeEmpty = f.includeEmptyArray(_headContext.hasCurrentIndex());
570576
f.filterFinishArray();
577+
if (includeEmpty) {
578+
return _nextBuffered(_headContext);
579+
}
571580
}
572581
_headContext = _headContext.getParent();
573582
_itemFilter = _headContext.getFilter();
@@ -576,6 +585,23 @@ protected final JsonToken _nextToken2() throws IOException
576585
}
577586
}
578587
continue main_loop;
588+
case ID_END_OBJECT:
589+
{
590+
boolean returnEnd = _headContext.isStartHandled();
591+
f = _headContext.getFilter();
592+
if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
593+
boolean includeEmpty = f.includeEmptyArray(_headContext.hasCurrentName());
594+
f.filterFinishObject();
595+
if (includeEmpty) {
596+
return _nextBuffered(_headContext);
597+
} }
598+
_headContext = _headContext.getParent();
599+
_itemFilter = _headContext.getFilter();
600+
if (returnEnd) {
601+
return (_currToken = t);
602+
}
603+
}
604+
continue main_loop;
579605

580606
case ID_FIELD_NAME:
581607
{
@@ -708,13 +734,16 @@ protected final JsonToken _nextTokenWithBuffering(final TokenFilterContext buffR
708734
continue main_loop;
709735

710736
case ID_END_ARRAY:
711-
case ID_END_OBJECT:
712737
{
713738
// Unlike with other loops, here we know that content was NOT
714739
// included (won't get this far otherwise)
715740
f = _headContext.getFilter();
716741
if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
742+
boolean includeEmpty = f.includeEmptyArray(_headContext.hasCurrentIndex());
717743
f.filterFinishArray();
744+
if (includeEmpty) {
745+
return _nextBuffered(buffRoot);
746+
}
718747
}
719748
boolean gotEnd = (_headContext == buffRoot);
720749
boolean returnEnd = gotEnd && _headContext.isStartHandled();
@@ -727,6 +756,33 @@ protected final JsonToken _nextTokenWithBuffering(final TokenFilterContext buffR
727756
}
728757
}
729758
continue main_loop;
759+
case ID_END_OBJECT:
760+
{
761+
// Unlike with other loops, here we know that content was NOT
762+
// included (won't get this far otherwise)
763+
f = _headContext.getFilter();
764+
if ((f != null) && (f != TokenFilter.INCLUDE_ALL)) {
765+
boolean includeEmpty = f.includeEmptyObject(_headContext.hasCurrentName());
766+
f.filterFinishObject();
767+
if (includeEmpty) {
768+
_headContext._currentName = _headContext._parent == null
769+
? null
770+
: _headContext._parent._currentName;
771+
_headContext._needToHandleName = false;
772+
return _nextBuffered(buffRoot);
773+
}
774+
}
775+
boolean gotEnd = (_headContext == buffRoot);
776+
boolean returnEnd = gotEnd && _headContext.isStartHandled();
777+
778+
_headContext = _headContext.getParent();
779+
_itemFilter = _headContext.getFilter();
780+
781+
if (returnEnd) {
782+
return t;
783+
}
784+
}
785+
continue main_loop;
730786

731787
case ID_FIELD_NAME:
732788
{

src/main/java/com/fasterxml/jackson/core/filter/TokenFilter.java

+8
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,14 @@ public boolean includeEmbeddedValue(Object value) {
432432
return _includeScalar();
433433
}
434434

435+
public boolean includeEmptyArray(boolean contentsFiltered) {
436+
return false;
437+
}
438+
439+
public boolean includeEmptyObject(boolean contentsFiltered) {
440+
return false;
441+
}
442+
435443
/*
436444
/**********************************************************
437445
/* Overrides

src/main/java/com/fasterxml/jackson/core/filter/TokenFilterContext.java

+20
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,16 @@ public TokenFilterContext closeArray(JsonGenerator gen) throws IOException
233233
{
234234
if (_startHandled) {
235235
gen.writeEndArray();
236+
} else {
237+
if ((_filter != null) && (_filter != TokenFilter.INCLUDE_ALL)) {
238+
if (_filter.includeEmptyArray(hasCurrentIndex())) {
239+
if (_parent != null) {
240+
_parent._writePath(gen);
241+
}
242+
gen.writeStartArray();
243+
gen.writeEndArray();
244+
}
245+
}
236246
}
237247
if ((_filter != null) && (_filter != TokenFilter.INCLUDE_ALL)) {
238248
_filter.filterFinishArray();
@@ -244,6 +254,16 @@ public TokenFilterContext closeObject(JsonGenerator gen) throws IOException
244254
{
245255
if (_startHandled) {
246256
gen.writeEndObject();
257+
} else {
258+
if ((_filter != null) && (_filter != TokenFilter.INCLUDE_ALL)) {
259+
if (_filter.includeEmptyObject(hasCurrentName())) {
260+
if (_parent != null) {
261+
_parent._writePath(gen);
262+
}
263+
gen.writeStartObject();
264+
gen.writeEndObject();
265+
}
266+
}
247267
}
248268
if ((_filter != null) && (_filter != TokenFilter.INCLUDE_ALL)) {
249269
_filter.filterFinishObject();

src/test/java/com/fasterxml/jackson/core/BaseTest.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -451,11 +451,15 @@ protected String readAndWrite(JsonFactory f, JsonParser p) throws IOException
451451
g.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT);
452452
try {
453453
while (p.nextToken() != null) {
454+
System.err.println(p.currentToken() + " " + p.currentName() + " " + p.currentValue());
454455
g.copyCurrentEvent(p);
455456
}
456457
} catch (IOException e) {
457458
g.flush();
458-
fail("Unexpected problem during `readAndWrite`. Output so far: '"+sw+"'; problem: "+e);
459+
throw new AssertionError(
460+
"Unexpected problem during `readAndWrite`. Output so far: '" +
461+
sw + "'; problem: " + e.getMessage(),
462+
e);
459463
}
460464
p.close();
461465
g.close();

0 commit comments

Comments
 (0)