From 5d3a256643f79263d52e8e40ba988c0a7022a126 Mon Sep 17 00:00:00 2001 From: Jonathan Haber Date: Fri, 6 Nov 2020 17:21:37 -0500 Subject: [PATCH] More customizable TokenFilter inclusion (#573) * Add support for writing empty objects and arrays * Get it working * Add some more tests * Switch to enum * Rename enum * Rename arg * Improve comment * Fix indentation * Rename constant * Rename to TokenFilterInclusion * Move enum to TokenFilter * Apply to parser as well * Add tests * Put back star imports * More star imports --- .../filter/FilteringGeneratorDelegate.java | 89 +++++--- .../core/filter/FilteringParserDelegate.java | 81 ++++--- .../jackson/core/filter/TokenFilter.java | 24 +++ .../core/filter/TokenFilterContext.java | 40 +--- .../filter/BasicGeneratorFilteringTest.java | 168 +++++++++++++-- .../core/filter/BasicParserFilteringTest.java | 199 ++++++++++++++++-- .../JsonPointerGeneratorFilteringTest.java | 79 +++---- .../JsonPointerParserFilteringTest.java | 57 ++--- .../core/json/async/AsyncTokenFilterTest.java | 5 +- .../jackson/core/write/UTF8GeneratorTest.java | 3 +- 10 files changed, 542 insertions(+), 203 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/core/filter/FilteringGeneratorDelegate.java b/src/main/java/com/fasterxml/jackson/core/filter/FilteringGeneratorDelegate.java index 4f9eafa81d..9e1f60bd9b 100644 --- a/src/main/java/com/fasterxml/jackson/core/filter/FilteringGeneratorDelegate.java +++ b/src/main/java/com/fasterxml/jackson/core/filter/FilteringGeneratorDelegate.java @@ -7,6 +7,7 @@ import java.math.BigInteger; import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.filter.TokenFilter.Inclusion; import com.fasterxml.jackson.core.util.JsonGeneratorDelegate; /** @@ -18,6 +19,7 @@ */ public class FilteringGeneratorDelegate extends JsonGeneratorDelegate { + /* /********************************************************** /* Configuration @@ -45,18 +47,7 @@ public class FilteringGeneratorDelegate extends JsonGeneratorDelegate * done and only explicitly included entries are output; if `true` then * path from main level down to match is also included as necessary. */ - protected boolean _includePath; - - /* NOTE: this feature is included in the first version (2.6), but - * there is no public API to enable it, yet, since there isn't an - * actual use case. But it seemed possible need could arise, which - * is feature has not yet been removed. If no use is found within - * first version or two, just remove. - * - * Marked as deprecated since its status is uncertain. - */ - @Deprecated - protected boolean _includeImmediateParent; + protected TokenFilter.Inclusion _inclusion; /* /********************************************************** @@ -90,8 +81,15 @@ public class FilteringGeneratorDelegate extends JsonGeneratorDelegate /********************************************************** */ + @Deprecated public FilteringGeneratorDelegate(JsonGenerator d, TokenFilter f, boolean includePath, boolean allowMultipleMatches) + { + this(d, f, includePath ? Inclusion.INCLUDE_ALL_AND_PATH : Inclusion.ONLY_INCLUDE_ALL, allowMultipleMatches); + } + + public FilteringGeneratorDelegate(JsonGenerator d, TokenFilter f, + TokenFilter.Inclusion inclusion, boolean allowMultipleMatches) { // By default, do NOT delegate copy methods super(d, false); @@ -99,7 +97,7 @@ public FilteringGeneratorDelegate(JsonGenerator d, TokenFilter f, // and this is the currently active filter for root values _itemFilter = f; _filterContext = TokenFilterContext.createRootContext(f); - _includePath = includePath; + _inclusion = inclusion; _allowMultipleMatches = allowMultipleMatches; } @@ -170,6 +168,10 @@ public void writeStartArray() throws IOException _checkParentPath(); _filterContext = _filterContext.createChildArrayContext(_itemFilter, true); delegate.writeStartArray(); + } else if (_itemFilter != null && _inclusion == Inclusion.INCLUDE_NON_NULL) { + _checkParentPath(false /* isMatch */); + _filterContext = _filterContext.createChildArrayContext(_itemFilter, true); + delegate.writeStartArray(); } else { _filterContext = _filterContext.createChildArrayContext(_itemFilter, false); } @@ -200,6 +202,10 @@ public void writeStartArray(int size) throws IOException _checkParentPath(); _filterContext = _filterContext.createChildArrayContext(_itemFilter, true); delegate.writeStartArray(size); + } else if (_itemFilter != null && _inclusion == Inclusion.INCLUDE_NON_NULL) { + _checkParentPath(false /* isMatch */); + _filterContext = _filterContext.createChildArrayContext(_itemFilter, true); + delegate.writeStartArray(size); } else { _filterContext = _filterContext.createChildArrayContext(_itemFilter, false); } @@ -298,6 +304,10 @@ public void writeStartObject() throws IOException _checkParentPath(); _filterContext = _filterContext.createChildObjectContext(f, true); delegate.writeStartObject(); + } else if (f != null && _inclusion == Inclusion.INCLUDE_NON_NULL) { + _checkParentPath(false /* isMatch */); + _filterContext = _filterContext.createChildObjectContext(f, true); + delegate.writeStartObject(); } else { // filter out _filterContext = _filterContext.createChildObjectContext(f, false); } @@ -328,6 +338,10 @@ public void writeStartObject(Object forValue) throws IOException _checkParentPath(); _filterContext = _filterContext.createChildObjectContext(f, true); delegate.writeStartObject(forValue); + } else if (f != null && _inclusion == Inclusion.INCLUDE_NON_NULL) { + _checkParentPath(false /* isMatch */); + _filterContext = _filterContext.createChildObjectContext(f, true); + delegate.writeStartObject(forValue); } else { // filter out _filterContext = _filterContext.createChildObjectContext(f, false); } @@ -441,7 +455,7 @@ public void writeString(String value) throws IOException } } _checkParentPath(); - } + } delegate.writeString(value); } @@ -463,7 +477,7 @@ public void writeString(char[] text, int offset, int len) throws IOException } } _checkParentPath(); - } + } delegate.writeString(text, offset, len); } @@ -484,7 +498,7 @@ public void writeString(SerializableString value) throws IOException } } _checkParentPath(); - } + } delegate.writeString(value); } @@ -637,7 +651,7 @@ public void writeNumber(short v) throws IOException } } _checkParentPath(); - } + } delegate.writeNumber(v); } @@ -658,7 +672,7 @@ public void writeNumber(int v) throws IOException } } _checkParentPath(); - } + } delegate.writeNumber(v); } @@ -679,7 +693,7 @@ public void writeNumber(long v) throws IOException } } _checkParentPath(); - } + } delegate.writeNumber(v); } @@ -700,7 +714,7 @@ public void writeNumber(BigInteger v) throws IOException } } _checkParentPath(); - } + } delegate.writeNumber(v); } @@ -721,7 +735,7 @@ public void writeNumber(double v) throws IOException } } _checkParentPath(); - } + } delegate.writeNumber(v); } @@ -742,7 +756,7 @@ public void writeNumber(float v) throws IOException } } _checkParentPath(); - } + } delegate.writeNumber(v); } @@ -763,7 +777,7 @@ public void writeNumber(BigDecimal v) throws IOException } } _checkParentPath(); - } + } delegate.writeNumber(v); } @@ -826,7 +840,7 @@ public void writeBoolean(boolean v) throws IOException } } _checkParentPath(); - } + } delegate.writeBoolean(v); } @@ -847,7 +861,7 @@ public void writeNull() throws IOException } } _checkParentPath(); - } + } delegate.writeNull(); } @@ -970,13 +984,23 @@ public void copyCurrentStructure(JsonParser jp) throws IOException { protected void _checkParentPath() throws IOException { - ++_matchCount; + _checkParentPath(true); + } + + protected void _checkParentPath(boolean isMatch) throws IOException + { + if (isMatch) { + ++_matchCount; + } // only need to construct path if parent wasn't written - if (_includePath) { + if (_inclusion == Inclusion.INCLUDE_ALL_AND_PATH) { _filterContext.writePath(delegate); + } else if (_inclusion == Inclusion.INCLUDE_NON_NULL) { + // path has already been written, except for maybe field name + _filterContext.ensureFieldNameWritten(delegate); } // also: if no multiple matches desired, short-cut checks - if (!_allowMultipleMatches) { + if (isMatch && !_allowMultipleMatches) { // Mark parents as "skip" so that further check calls are not made _filterContext.skipParentChecks(); } @@ -990,12 +1014,11 @@ protected void _checkParentPath() throws IOException protected void _checkPropertyParentPath() throws IOException { ++_matchCount; - if (_includePath) { + if (_inclusion == Inclusion.INCLUDE_ALL_AND_PATH) { _filterContext.writePath(delegate); - } else if (_includeImmediateParent) { - // 21-Apr-2015, tatu: Note that there is no API to enable this currently... - // retained for speculative future use - _filterContext.writeImmediatePath(delegate); + } else if (_inclusion == Inclusion.INCLUDE_NON_NULL) { + // path has already been written, except for maybe field name + _filterContext.ensureFieldNameWritten(delegate); } // also: if no multiple matches desired, short-cut checks diff --git a/src/main/java/com/fasterxml/jackson/core/filter/FilteringParserDelegate.java b/src/main/java/com/fasterxml/jackson/core/filter/FilteringParserDelegate.java index abc1a38206..014da5ecb2 100644 --- a/src/main/java/com/fasterxml/jackson/core/filter/FilteringParserDelegate.java +++ b/src/main/java/com/fasterxml/jackson/core/filter/FilteringParserDelegate.java @@ -6,6 +6,7 @@ import java.math.BigInteger; import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.filter.TokenFilter.Inclusion; import com.fasterxml.jackson.core.util.JsonParserDelegate; import static com.fasterxml.jackson.core.JsonTokenId.*; @@ -46,19 +47,8 @@ public class FilteringParserDelegate extends JsonParserDelegate * done and only explicitly included entries are output; if `true` then * path from main level down to match is also included as necessary. */ - protected boolean _includePath; - - /* NOTE: this feature is included in the first version (2.6), but - * there is no public API to enable it, yet, since there isn't an - * actual use case. But it seemed possible need could arise, which - * is feature has not yet been removed. If no use is found within - * first version or two, just remove. - * - * Marked as deprecated since its status is uncertain. - */ - @Deprecated - protected boolean _includeImmediateParent; - + protected TokenFilter.Inclusion _inclusion; + /* /********************************************************** /* State @@ -111,15 +101,22 @@ public class FilteringParserDelegate extends JsonParserDelegate /********************************************************** */ + @Deprecated public FilteringParserDelegate(JsonParser p, TokenFilter f, boolean includePath, boolean allowMultipleMatches) + { + this(p, f, includePath ? Inclusion.INCLUDE_ALL_AND_PATH : Inclusion.ONLY_INCLUDE_ALL, allowMultipleMatches); + } + + public FilteringParserDelegate(JsonParser p, TokenFilter f, + TokenFilter.Inclusion inclusion, boolean allowMultipleMatches) { super(p); rootFilter = f; // and this is the currently active filter for root values _itemFilter = f; _headContext = TokenFilterContext.createRootContext(f); - _includePath = includePath; + _inclusion = inclusion; _allowMultipleMatches = allowMultipleMatches; } @@ -235,9 +232,10 @@ public JsonToken nextToken() throws IOException // If all the conditions matches then check for scalar / non-scalar property if (!_allowMultipleMatches && (_currToken != null) && (_exposedContext == null)) { - // if scalar, and scalar not present in obj/array and !includePath and INCLUDE_ALL - // matched once, return null - if (_currToken.isScalarValue() && !_headContext.isStartHandled() && !_includePath + // if scalar, and scalar not present in obj/array and _inclusion == ONLY_INCLUDE_ALL + // and INCLUDE_ALL matched once, return null + if (_currToken.isScalarValue() && !_headContext.isStartHandled() + && _inclusion == Inclusion.ONLY_INCLUDE_ALL && (_itemFilter == TokenFilter.INCLUDE_ALL)) { return (_currToken = null); } @@ -318,11 +316,15 @@ public JsonToken nextToken() throws IOException if (f == TokenFilter.INCLUDE_ALL) { _headContext = _headContext.createChildArrayContext(f, true); return (_currToken = t); + } else if (f != null && _inclusion == Inclusion.INCLUDE_NON_NULL) { + // TODO don't count as match? + _headContext = _headContext.createChildArrayContext(f, true); + return (_currToken = t); } _headContext = _headContext.createChildArrayContext(f, false); - + // Also: only need buffering if parent path to be included - if (_includePath) { + if (_inclusion == Inclusion.INCLUDE_ALL_AND_PATH) { t = _nextTokenWithBuffering(_headContext); if (t != null) { _currToken = t; @@ -354,10 +356,14 @@ public JsonToken nextToken() throws IOException if (f == TokenFilter.INCLUDE_ALL) { _headContext = _headContext.createChildObjectContext(f, true); return (_currToken = t); + } else if (f != null && _inclusion == Inclusion.INCLUDE_NON_NULL) { + // TODO don't count as match? + _headContext = _headContext.createChildObjectContext(f, true); + return (_currToken = t); } _headContext = _headContext.createChildObjectContext(f, false); // Also: only need buffering if parent path to be included - if (_includePath) { + if (_inclusion == Inclusion.INCLUDE_ALL_AND_PATH) { t = _nextTokenWithBuffering(_headContext); if (t != null) { _currToken = t; @@ -391,14 +397,6 @@ public JsonToken nextToken() throws IOException f = _headContext.setFieldName(name); if (f == TokenFilter.INCLUDE_ALL) { _itemFilter = f; - if (!_includePath) { - // Minor twist here: if parent NOT included, may need to induce output of - // surrounding START_OBJECT/END_OBJECT - if (_includeImmediateParent && !_headContext.isStartHandled()) { - t = _headContext.nextTokenToRead(); // returns START_OBJECT but also marks it handled - _exposedContext = _headContext; - } - } return (_currToken = t); } if (f == null) { @@ -415,7 +413,7 @@ public JsonToken nextToken() throws IOException _itemFilter = f; if (f == TokenFilter.INCLUDE_ALL) { if (_verifyAllowedMatches()) { - if (_includePath) { + if (_inclusion == Inclusion.INCLUDE_ALL_AND_PATH) { return (_currToken = t); } } else { @@ -423,7 +421,7 @@ public JsonToken nextToken() throws IOException delegate.skipChildren(); } } - if (_includePath) { + if (_inclusion != Inclusion.ONLY_INCLUDE_ALL) { t = _nextTokenWithBuffering(_headContext); if (t != null) { _currToken = t; @@ -496,10 +494,13 @@ protected final JsonToken _nextToken2() throws IOException if (f == TokenFilter.INCLUDE_ALL) { _headContext = _headContext.createChildArrayContext(f, true); return (_currToken = t); + } else if (f != null && _inclusion == Inclusion.INCLUDE_NON_NULL) { + _headContext = _headContext.createChildArrayContext(f, true); + return (_currToken = t); } _headContext = _headContext.createChildArrayContext(f, false); // but if we didn't figure it out yet, need to buffer possible events - if (_includePath) { + if (_inclusion == Inclusion.INCLUDE_ALL_AND_PATH) { t = _nextTokenWithBuffering(_headContext); if (t != null) { _currToken = t; @@ -531,9 +532,12 @@ protected final JsonToken _nextToken2() throws IOException if (f == TokenFilter.INCLUDE_ALL) { _headContext = _headContext.createChildObjectContext(f, true); return (_currToken = t); + } else if (f != null && _inclusion == Inclusion.INCLUDE_NON_NULL) { + _headContext = _headContext.createChildObjectContext(f, true); + return (_currToken = t); } _headContext = _headContext.createChildObjectContext(f, false); - if (_includePath) { + if (_inclusion == Inclusion.INCLUDE_ALL_AND_PATH) { t = _nextTokenWithBuffering(_headContext); if (t != null) { _currToken = t; @@ -579,13 +583,12 @@ protected final JsonToken _nextToken2() throws IOException } _itemFilter = f; if (f == TokenFilter.INCLUDE_ALL) { - if (_verifyAllowedMatches() && _includePath) { + if (_verifyAllowedMatches() && _inclusion == Inclusion.INCLUDE_ALL_AND_PATH) { return (_currToken = t); } -// if (_includeImmediateParent) { ... continue main_loop; } - if (_includePath) { + if (_inclusion != Inclusion.ONLY_INCLUDE_ALL) { t = _nextTokenWithBuffering(_headContext); if (t != null) { _currToken = t; @@ -647,6 +650,10 @@ protected final JsonToken _nextTokenWithBuffering(final TokenFilterContext buffR if (f == TokenFilter.INCLUDE_ALL) { _headContext = _headContext.createChildArrayContext(f, true); return _nextBuffered(buffRoot); + } else if (f != null && _inclusion == Inclusion.INCLUDE_NON_NULL) { + // TODO don't count as match? + _headContext = _headContext.createChildArrayContext(f, true); + return _nextBuffered(buffRoot); } _headContext = _headContext.createChildArrayContext(f, false); continue main_loop; @@ -674,6 +681,10 @@ protected final JsonToken _nextTokenWithBuffering(final TokenFilterContext buffR if (f == TokenFilter.INCLUDE_ALL) { _headContext = _headContext.createChildObjectContext(f, true); return _nextBuffered(buffRoot); + } else if (f != null && _inclusion == Inclusion.INCLUDE_NON_NULL) { + // TODO don't count as match? + _headContext = _headContext.createChildArrayContext(f, true); + return _nextBuffered(buffRoot); } _headContext = _headContext.createChildObjectContext(f, false); continue main_loop; diff --git a/src/main/java/com/fasterxml/jackson/core/filter/TokenFilter.java b/src/main/java/com/fasterxml/jackson/core/filter/TokenFilter.java index f308388629..0624ca1636 100644 --- a/src/main/java/com/fasterxml/jackson/core/filter/TokenFilter.java +++ b/src/main/java/com/fasterxml/jackson/core/filter/TokenFilter.java @@ -16,6 +16,30 @@ public class TokenFilter { + /** + * Enumeration that controls how TokenFilter return values are interpreted + * + * @since 2.11 + */ + public enum Inclusion { + /** + * Tokens will only be included if the filter returns TokenFilter.INCLUDE_ALL + */ + ONLY_INCLUDE_ALL, + /** + * When TokenFilter.INCLUDE_ALL is returned, the corresponding token will + * be included as well as enclosing tokens up to the root + */ + INCLUDE_ALL_AND_PATH, + /** + * Tokens will be included if any non-null filter is returned. + * The exception is if a field name returns a non-null filter, + * but the field value returns a null filter. In this case the + * field name and value will both be omitted. + */ + INCLUDE_NON_NULL + } + // // Marker values /** diff --git a/src/main/java/com/fasterxml/jackson/core/filter/TokenFilterContext.java b/src/main/java/com/fasterxml/jackson/core/filter/TokenFilterContext.java index c414633ee6..70c8a61b5c 100644 --- a/src/main/java/com/fasterxml/jackson/core/filter/TokenFilterContext.java +++ b/src/main/java/com/fasterxml/jackson/core/filter/TokenFilterContext.java @@ -147,6 +147,17 @@ public TokenFilter checkValue(TokenFilter filter) { return filter.includeRootValue(ix); } + /** + * Method called to ensure that field name, if present, has been written + */ + public void ensureFieldNameWritten(JsonGenerator gen) throws IOException + { + if (_needToHandleName) { + _needToHandleName = false; + gen.writeFieldName(_currentName); + } + } + /** * Method called to ensure that parent path from root is written up to * and including this node. @@ -175,35 +186,6 @@ public void writePath(JsonGenerator gen) throws IOException } } - /** - * Variant of {@link #writePath(JsonGenerator)} called when all we - * need is immediately surrounding Object. Method typically called - * when including a single property but not including full path - * to root. - */ - public void writeImmediatePath(JsonGenerator gen) throws IOException - { - if ((_filter == null) || (_filter == TokenFilter.INCLUDE_ALL)) { - return; - } - if (_startHandled) { - // even if Object started, need to start leaf-level name - if (_needToHandleName) { - gen.writeFieldName(_currentName); - } - } else { - _startHandled = true; - if (_type == TYPE_OBJECT) { - gen.writeStartObject(); - if (_needToHandleName) { - gen.writeFieldName(_currentName); - } - } else if (_type == TYPE_ARRAY) { - gen.writeStartArray(); - } - } - } - private void _writePath(JsonGenerator gen) throws IOException { if ((_filter == null) || (_filter == TokenFilter.INCLUDE_ALL)) { diff --git a/src/test/java/com/fasterxml/jackson/core/filter/BasicGeneratorFilteringTest.java b/src/test/java/com/fasterxml/jackson/core/filter/BasicGeneratorFilteringTest.java index ab3e395985..07f125122a 100644 --- a/src/test/java/com/fasterxml/jackson/core/filter/BasicGeneratorFilteringTest.java +++ b/src/test/java/com/fasterxml/jackson/core/filter/BasicGeneratorFilteringTest.java @@ -6,6 +6,7 @@ import java.util.*; import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.filter.TokenFilter.Inclusion; import com.fasterxml.jackson.core.io.SerializedString; /** @@ -67,6 +68,23 @@ public TokenFilter includeProperty(String name) { } } + static class StrictNameMatchFilter extends TokenFilter + { + private final Set _names; + + public StrictNameMatchFilter(String... names) { + _names = new HashSet(Arrays.asList(names)); + } + + @Override + public TokenFilter includeProperty(String name) { + if (_names.contains(name)) { + return TokenFilter.INCLUDE_ALL; + } + return null; + } + } + static class IndexMatchFilter extends TokenFilter { private final BitSet _indices; @@ -95,6 +113,22 @@ public TokenFilter includeElement(int index) { protected boolean _includeScalar() { return false; } } + static class NoArraysFilter extends TokenFilter + { + @Override + public TokenFilter filterStartArray() { + return null; + } + } + + static class NoObjectsFilter extends TokenFilter + { + @Override + public TokenFilter filterStartObject() { + return null; + } + } + /* /********************************************************** /* Test methods @@ -120,7 +154,7 @@ public void testSingleMatchFilteringWithoutPath() throws Exception StringWriter w = new StringWriter(); JsonGenerator gen = new FilteringGeneratorDelegate(_createGenerator(w), new NameMatchFilter("value"), - false, // includePath + Inclusion.ONLY_INCLUDE_ALL, false // multipleMatches ); final String JSON = "{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'b':true}"; @@ -141,7 +175,7 @@ public void testSingleMatchFilteringWithPath() throws Exception NameMatchFilter filter = new NameMatchFilter("value"); FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(origGen, filter, - true, // includePath + Inclusion.INCLUDE_ALL_AND_PATH, false // multipleMatches ); @@ -163,7 +197,7 @@ public void testSingleMatchFilteringWithPathSkippedArray() throws Exception NameMatchFilter filter = new NameMatchFilter("value"); FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(origGen, filter, - true, // includePath + Inclusion.INCLUDE_ALL_AND_PATH, false // multipleMatches ); @@ -192,8 +226,8 @@ private void _testSingleMatchFilteringWithPathAlternate1(boolean exclude) throws : new NameMatchFilter("value"); FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(_createGenerator(w), tf, - true, // includePath - true // multipleMatches + Inclusion.INCLUDE_ALL_AND_PATH, + false // multipleMatches ); //final String JSON = "{'a':123,'array':[1,2],'ob':{'value0':2,'value':[3],'value2':'foo'},'b':true}"; @@ -239,7 +273,7 @@ public void testSingleMatchFilteringWithPathRawBinary() throws Exception StringWriter w = new StringWriter(); FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(_createGenerator(w), new NameMatchFilter("array"), - true, // includePath + Inclusion.INCLUDE_ALL_AND_PATH, false // multipleMatches ); //final String JSON = "{'header':['ENCODED',raw],'array':['base64stuff',1,2,3,4,5,6.25,7.5],'extra':[1,2,3,4,5,6.25,7.5]}"; @@ -290,7 +324,7 @@ public void testMultipleMatchFilteringWithPath1() throws Exception StringWriter w = new StringWriter(); FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(_createGenerator(w), new NameMatchFilter("value0", "value2"), - true, /* includePath */ true /* multipleMatches */ ); + Inclusion.INCLUDE_ALL_AND_PATH, true /* multipleMatches */ ); final String JSON = "{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'b':true}"; writeJsonDoc(JSON_F, JSON, gen); assertEquals(aposToQuotes("{'ob':{'value0':2,'value2':4}}"), w.toString()); @@ -318,7 +352,7 @@ public void testMultipleMatchFilteringWithPath2() throws Exception FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(_createGenerator(w), new NameMatchFilter("array", "b", "value"), - true, true); + Inclusion.INCLUDE_ALL_AND_PATH, true); final String JSON = "{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'b':true}"; writeJsonDoc(JSON_F, JSON, gen); assertEquals(aposToQuotes("{'array':[1,2],'ob':{'value':3},'b':true}"), w.toString()); @@ -331,13 +365,108 @@ public void testMultipleMatchFilteringWithPath3() throws Exception FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(_createGenerator(w), new NameMatchFilter("value"), - true, true); + Inclusion.INCLUDE_ALL_AND_PATH, true); final String JSON = "{'root':{'a0':true,'a':{'value':3},'b':{'value':'abc'}},'b0':false}"; writeJsonDoc(JSON_F, JSON, gen); assertEquals(aposToQuotes("{'root':{'a':{'value':3},'b':{'value':'abc'}}}"), w.toString()); assertEquals(2, gen.getMatchCount()); } + public void testNoMatchFiltering1() throws Exception + { + StringWriter w = new StringWriter(); + + FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(w), + new NameMatchFilter("invalid"), + Inclusion.INCLUDE_NON_NULL, true); + final String JSON = "{'root':{'a0':true,'b':{'value':4}},'b0':false}"; + writeJsonDoc(JSON_F, JSON, gen); + assertEquals(aposToQuotes("{'root':{'b':{}}}"), w.toString()); + assertEquals(0, gen.getMatchCount()); + } + + public void testNoMatchFiltering2() throws Exception + { + StringWriter w = new StringWriter(); + + FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(w), + new NameMatchFilter("invalid"), + Inclusion.INCLUDE_NON_NULL, true); + final String object = "{'root':{'a0':true,'b':{'value':4}},'b0':false}"; + final String JSON = String.format("[%s,%s,%s]", object, object, object); + writeJsonDoc(JSON_F, JSON, gen); + assertEquals(aposToQuotes("[{'root':{'b':{}}},{'root':{'b':{}}},{'root':{'b':{}}}]"), w.toString()); + assertEquals(0, gen.getMatchCount()); + } + + public void testNoMatchFiltering3() throws Exception + { + StringWriter w = new StringWriter(); + + FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(w), + new NameMatchFilter("invalid"), + Inclusion.INCLUDE_NON_NULL, true); + final String object = "{'root':{'a0':true,'b':{'value':4}},'b0':false}"; + final String JSON = String.format("[[%s],[%s],[%s]]", object, object, object); + writeJsonDoc(JSON_F, JSON, gen); + assertEquals(aposToQuotes("[[{'root':{'b':{}}}],[{'root':{'b':{}}}],[{'root':{'b':{}}}]]"), w.toString()); + assertEquals(0, gen.getMatchCount()); + } + + public void testNoMatchFiltering4() throws Exception + { + StringWriter w = new StringWriter(); + + FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(w), + new StrictNameMatchFilter("invalid"), + Inclusion.INCLUDE_NON_NULL, true); + final String JSON = "{'root':{'a0':true,'a':{'value':3},'b':{'value':4}},'b0':false}"; + writeJsonDoc(JSON_F, JSON, gen); + assertEquals(aposToQuotes("{}"), w.toString()); + assertEquals(0, gen.getMatchCount()); + } + + public void testNoMatchFiltering5() throws Exception + { + StringWriter w = new StringWriter(); + + FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(w), + new StrictNameMatchFilter("invalid"), + Inclusion.INCLUDE_NON_NULL, true); + final String object = "{'root':{'a0':true,'b':{'value':4}},'b0':false}"; + final String JSON = String.format("[%s,%s,%s]", object, object, object); + writeJsonDoc(JSON_F, JSON, gen); + assertEquals(aposToQuotes("[{},{},{}]"), w.toString()); + assertEquals(0, gen.getMatchCount()); + } + + public void testNoMatchFiltering6() throws Exception + { + StringWriter w = new StringWriter(); + + FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(w), + new StrictNameMatchFilter("invalid"), + Inclusion.INCLUDE_NON_NULL, true); + final String object = "{'root':{'a0':true,'b':{'value':4}},'b0':false}"; + final String JSON = String.format("[[%s],[%s],[%s]]", object, object, object); + writeJsonDoc(JSON_F, JSON, gen); + assertEquals(aposToQuotes("[[{}],[{}],[{}]]"), w.toString()); + assertEquals(0, gen.getMatchCount()); + } + + public void testValueOmitsFieldName1() throws Exception + { + StringWriter w = new StringWriter(); + + FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(w), + new NoArraysFilter(), + Inclusion.INCLUDE_NON_NULL, true); + final String JSON = "{'root':['a'],'b0':false}"; + writeJsonDoc(JSON_F, JSON, gen); + assertEquals(aposToQuotes("{'b0':false}"), w.toString()); + assertEquals(1, gen.getMatchCount()); + } + public void testMultipleMatchFilteringWithPath4() throws Exception { StringWriter w = new StringWriter(); @@ -350,12 +479,25 @@ public void testMultipleMatchFilteringWithPath4() throws Exception assertEquals(1, gen.getMatchCount()); } + public void testValueOmitsFieldName2() throws Exception + { + StringWriter w = new StringWriter(); + + FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(JSON_F.createGenerator(w), + new NoObjectsFilter(), + Inclusion.INCLUDE_NON_NULL, true); + final String JSON = "['a',{'root':{'b':{'value':4}},'b0':false}]"; + writeJsonDoc(JSON_F, JSON, gen); + assertEquals(aposToQuotes("['a']"), w.toString()); + assertEquals(1, gen.getMatchCount()); + } + public void testIndexMatchWithPath1() throws Exception { StringWriter w = new StringWriter(); FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(_createGenerator(w), new IndexMatchFilter(1), - true, true); + Inclusion.INCLUDE_ALL_AND_PATH, true); final String JSON = "{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':'abc'},'b':true}"; writeJsonDoc(JSON_F, JSON, gen); assertEquals(aposToQuotes("{'array':[2]}"), w.toString()); @@ -363,7 +505,7 @@ public void testIndexMatchWithPath1() throws Exception w = new StringWriter(); gen = new FilteringGeneratorDelegate(_createGenerator(w), new IndexMatchFilter(0), - true, true); + Inclusion.INCLUDE_ALL_AND_PATH, true); writeJsonDoc(JSON_F, JSON, gen); assertEquals(aposToQuotes("{'array':[1]}"), w.toString()); assertEquals(1, gen.getMatchCount()); @@ -374,7 +516,7 @@ public void testIndexMatchWithPath2() throws Exception StringWriter w = new StringWriter(); FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(_createGenerator(w), new IndexMatchFilter(0,1), - true, true); + Inclusion.INCLUDE_ALL_AND_PATH, true); String JSON = "{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'b':true}"; writeJsonDoc(JSON_F, JSON, gen); assertEquals(aposToQuotes("{'array':[1,2]}"), w.toString()); @@ -415,7 +557,7 @@ public void testWriteStartObjectWithObject() throws Exception FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(_createGenerator(w), TokenFilter.INCLUDE_ALL, - true, true); + Inclusion.INCLUDE_ALL_AND_PATH, true); String value = "val"; diff --git a/src/test/java/com/fasterxml/jackson/core/filter/BasicParserFilteringTest.java b/src/test/java/com/fasterxml/jackson/core/filter/BasicParserFilteringTest.java index e019afbcfb..fb17bde17d 100644 --- a/src/test/java/com/fasterxml/jackson/core/filter/BasicParserFilteringTest.java +++ b/src/test/java/com/fasterxml/jackson/core/filter/BasicParserFilteringTest.java @@ -1,9 +1,11 @@ package com.fasterxml.jackson.core.filter; +import java.io.StringWriter; import java.math.BigInteger; import java.util.*; import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.filter.TokenFilter.Inclusion; @SuppressWarnings("resource") public class BasicParserFilteringTest extends BaseTest @@ -33,6 +35,23 @@ public TokenFilter includeProperty(String name) { protected boolean _includeScalar() { return false; } } + static class StrictNameMatchFilter extends TokenFilter + { + private final Set _names; + + public StrictNameMatchFilter(String... names) { + _names = new HashSet(Arrays.asList(names)); + } + + @Override + public TokenFilter includeProperty(String name) { + if (_names.contains(name)) { + return TokenFilter.INCLUDE_ALL; + } + return null; + } + } + static class IndexMatchFilter extends TokenFilter { private final BitSet _indices; @@ -61,6 +80,22 @@ public TokenFilter includeElement(int index) { protected boolean _includeScalar() { return false; } } + static class NoArraysFilter extends TokenFilter + { + @Override + public TokenFilter filterStartArray() { + return null; + } + } + + static class NoObjectsFilter extends TokenFilter + { + @Override + public TokenFilter filterStartObject() { + return null; + } + } + /* /********************************************************** /* Test methods @@ -84,7 +119,7 @@ public void testSingleMatchFilteringWithoutPath() throws Exception JsonParser p0 = JSON_F.createParser(SIMPLE); FilteringParserDelegate p = new FilteringParserDelegate(p0, new NameMatchFilter("value"), - false, // includePath + Inclusion.ONLY_INCLUDE_ALL, false // multipleMatches ); String result = readAndWrite(JSON_F, p); @@ -98,7 +133,7 @@ public void testSingleMatchFilteringWithPath1() throws Exception JsonParser p0 = JSON_F.createParser(jsonString); FilteringParserDelegate p = new FilteringParserDelegate(p0, new NameMatchFilter("a"), - true, // includePath + Inclusion.INCLUDE_ALL_AND_PATH, false // multipleMatches ); String result = readAndWrite(JSON_F, p); @@ -112,7 +147,7 @@ public void testSingleMatchFilteringWithPath2() throws Exception JsonParser p0 = JSON_F.createParser(jsonString); FilteringParserDelegate p = new FilteringParserDelegate(p0, new NameMatchFilter("value"), - true, // includePath + Inclusion.INCLUDE_ALL_AND_PATH, false // multipleMatches ); String result = readAndWrite(JSON_F, p); @@ -126,7 +161,7 @@ public void testSingleMatchFilteringWithPath3() throws Exception JsonParser p0 = JSON_F.createParser(jsonString); FilteringParserDelegate p = new FilteringParserDelegate(p0, new NameMatchFilter("ob"), - true, // includePath + Inclusion.INCLUDE_ALL_AND_PATH, false // multipleMatches ); String result = readAndWrite(JSON_F, p); @@ -140,7 +175,7 @@ public void testNotAllowMultipleMatchesWithoutPath1() throws Exception JsonParser p0 = JSON_F.createParser(jsonString); FilteringParserDelegate p = new FilteringParserDelegate(p0, new NameMatchFilter("value"), - false, // includePath + Inclusion.ONLY_INCLUDE_ALL, false // multipleMatches -false ); String result = readAndWrite(JSON_F, p); @@ -154,7 +189,7 @@ public void testNotAllowMultipleMatchesWithoutPath2() throws Exception JsonParser p0 = JSON_F.createParser(jsonString); FilteringParserDelegate p = new FilteringParserDelegate(p0, new IndexMatchFilter(1), - false, // includePath + Inclusion.ONLY_INCLUDE_ALL, false // multipleMatches -false ); String result = readAndWrite(JSON_F, p); @@ -168,7 +203,7 @@ public void testNotAllowMultipleMatchesWithPath1() throws Exception JsonParser p0 = JSON_F.createParser(jsonString); FilteringParserDelegate p = new FilteringParserDelegate(p0, new IndexMatchFilter(1), - true, // includePath + Inclusion.INCLUDE_ALL_AND_PATH, false // multipleMatches -false ); String result = readAndWrite(JSON_F, p); @@ -183,7 +218,7 @@ public void testNotAllowMultipleMatchesWithPath2() throws Exception JsonParser p0 = JSON_F.createParser(jsonString); FilteringParserDelegate p = new FilteringParserDelegate(p0, new IndexMatchFilter(1), - true, // includePath + Inclusion.INCLUDE_ALL_AND_PATH, false // multipleMatches -false ); String result = readAndWrite(JSON_F, p); @@ -197,7 +232,7 @@ public void testNotAllowMultipleMatchesWithPath3() throws Exception JsonParser p0 = JSON_F.createParser(jsonString); FilteringParserDelegate p = new FilteringParserDelegate(p0, new NameMatchFilter("value"), - true, // includePath + Inclusion.INCLUDE_ALL_AND_PATH, false // multipleMatches -false ); String result = readAndWrite(JSON_F, p); @@ -211,7 +246,7 @@ public void testNotAllowMultipleMatchesWithPath4() throws Exception JsonParser p0 = JSON_F.createParser(jsonString); FilteringParserDelegate p = new FilteringParserDelegate(p0, new NameMatchFilter("ob"), - true, // includePath + Inclusion.INCLUDE_ALL_AND_PATH, false // multipleMatches -false ); String result = readAndWrite(JSON_F, p); @@ -225,7 +260,7 @@ public void testAllowMultipleMatchesWithoutPath() throws Exception JsonParser p0 = JSON_F.createParser(jsonString); FilteringParserDelegate p = new FilteringParserDelegate(p0, new NameMatchFilter("value"), - false, // includePath + Inclusion.ONLY_INCLUDE_ALL, true // multipleMatches - true ); String result = readAndWrite(JSON_F, p); @@ -239,7 +274,7 @@ public void testAllowMultipleMatchesWithPath1() throws Exception JsonParser p0 = JSON_F.createParser(jsonString); FilteringParserDelegate p = new FilteringParserDelegate(p0, new NameMatchFilter("value"), - true, // includePath + Inclusion.INCLUDE_ALL_AND_PATH, true // multipleMatches - true ); String result = readAndWrite(JSON_F, p); @@ -253,7 +288,7 @@ public void testAllowMultipleMatchesWithPath2() throws Exception JsonParser p0 = JSON_F.createParser(jsonString); FilteringParserDelegate p = new FilteringParserDelegate(p0, new IndexMatchFilter(1), - true, // includePath + Inclusion.INCLUDE_ALL_AND_PATH, true // multipleMatches - true ); String result = readAndWrite(JSON_F, p); @@ -266,7 +301,7 @@ public void testMultipleMatchFilteringWithPath1() throws Exception JsonParser p0 = JSON_F.createParser(SIMPLE); FilteringParserDelegate p = new FilteringParserDelegate(p0, new NameMatchFilter("value0", "value2"), - true, /* includePath */ true /* multipleMatches */ ); + Inclusion.INCLUDE_ALL_AND_PATH, true /* multipleMatches */ ); String result = readAndWrite(JSON_F, p); assertEquals(aposToQuotes("{'ob':{'value0':2,'value2':0.25}}"), result); assertEquals(2, p.getMatchCount()); @@ -279,7 +314,7 @@ public void testMultipleMatchFilteringWithPath2() throws Exception JsonParser p0 = JSON_F.createParser(INPUT); FilteringParserDelegate p = new FilteringParserDelegate(p0, new NameMatchFilter("b", "value"), - true, true); + Inclusion.INCLUDE_ALL_AND_PATH, true); String result = readAndWrite(JSON_F, p); assertEquals(aposToQuotes("{'ob':{'value':3},'b':true}"), result); @@ -292,22 +327,140 @@ public void testMultipleMatchFilteringWithPath3() throws Exception JsonParser p0 = JSON_F.createParser(JSON); FilteringParserDelegate p = new FilteringParserDelegate(p0, new NameMatchFilter("value"), - true, true); + Inclusion.INCLUDE_ALL_AND_PATH, true); String result = readAndWrite(JSON_F, p); assertEquals(aposToQuotes("{'root':{'a':{'value':3},'b':{'value':\"foo\"}}}"), result); assertEquals(2, p.getMatchCount()); } + public void testNoMatchFiltering1() throws Exception + { + String jsonString = aposToQuotes("{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'b':true}"); + JsonParser p0 = JSON_F.createParser(jsonString); + FilteringParserDelegate p = new FilteringParserDelegate(p0, + new NameMatchFilter("invalid"), + Inclusion.INCLUDE_NON_NULL, + true // multipleMatches + ); + String result = readAndWrite(JSON_F, p); + assertEquals(aposToQuotes("{'array':[],'ob':{}}"), result); + assertEquals(0, p.getMatchCount()); + } + + public void testNoMatchFiltering2() throws Exception + { + String object = aposToQuotes("{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'b':true}"); + String jsonString = String.format("[%s,%s,%s]", object, object, object); + JsonParser p0 = JSON_F.createParser(jsonString); + FilteringParserDelegate p = new FilteringParserDelegate(p0, + new NameMatchFilter("invalid"), + Inclusion.INCLUDE_NON_NULL, + true // multipleMatches + ); + String result = readAndWrite(JSON_F, p); + assertEquals(aposToQuotes("[{'array':[],'ob':{}},{'array':[],'ob':{}},{'array':[],'ob':{}}]"), result); + assertEquals(0, p.getMatchCount()); + } + + public void testNoMatchFiltering3() throws Exception + { + String object = aposToQuotes("{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'b':true}"); + String jsonString = String.format("[[%s],[%s],[%s]]", object, object, object); + JsonParser p0 = JSON_F.createParser(jsonString); + FilteringParserDelegate p = new FilteringParserDelegate(p0, + new NameMatchFilter("invalid"), + Inclusion.INCLUDE_NON_NULL, + true // multipleMatches + ); + String result = readAndWrite(JSON_F, p); + assertEquals(aposToQuotes("[[{'array':[],'ob':{}}],[{'array':[],'ob':{}}],[{'array':[],'ob':{}}]]"), result); + assertEquals(0, p.getMatchCount()); + + StringWriter w = new StringWriter(); + } + + public void testNoMatchFiltering4() throws Exception + { + String jsonString = aposToQuotes("{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'b':true}"); + JsonParser p0 = JSON_F.createParser(jsonString); + FilteringParserDelegate p = new FilteringParserDelegate(p0, + new StrictNameMatchFilter("invalid"), + Inclusion.INCLUDE_NON_NULL, + true // multipleMatches + ); + String result = readAndWrite(JSON_F, p); + assertEquals(aposToQuotes("{}"), result); + assertEquals(0, p.getMatchCount()); + } + + public void testNoMatchFiltering5() throws Exception + { + String object = aposToQuotes("{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'b':true}"); + String jsonString = String.format("[%s,%s,%s]", object, object, object); + JsonParser p0 = JSON_F.createParser(jsonString); + FilteringParserDelegate p = new FilteringParserDelegate(p0, + new StrictNameMatchFilter("invalid"), + Inclusion.INCLUDE_NON_NULL, + true // multipleMatches + ); + String result = readAndWrite(JSON_F, p); + assertEquals(aposToQuotes("[{},{},{}]"), result); + assertEquals(0, p.getMatchCount()); + } + + public void testNoMatchFiltering6() throws Exception + { + String object = aposToQuotes("{'a':123,'array':[1,2],'ob':{'value0':2,'value':3,'value2':4},'b':true}"); + String jsonString = String.format("[[%s],[%s],[%s]]", object, object, object); + JsonParser p0 = JSON_F.createParser(jsonString); + FilteringParserDelegate p = new FilteringParserDelegate(p0, + new StrictNameMatchFilter("invalid"), + Inclusion.INCLUDE_NON_NULL, + true // multipleMatches + ); + String result = readAndWrite(JSON_F, p); + assertEquals(aposToQuotes("[[{}],[{}],[{}]]"), result); + assertEquals(0, p.getMatchCount()); + } + + public void testValueOmitsFieldName1() throws Exception + { + String jsonString = aposToQuotes("{'a':123,'array':[1,2]}"); + JsonParser p0 = JSON_F.createParser(jsonString); + FilteringParserDelegate p = new FilteringParserDelegate(p0, + new NoArraysFilter(), + Inclusion.INCLUDE_NON_NULL, + true // multipleMatches + ); + String result = readAndWrite(JSON_F, p); + assertEquals(aposToQuotes("{'a':123}"), result); + assertEquals(0, p.getMatchCount()); + } + + public void testValueOmitsFieldName2() throws Exception + { + String jsonString = aposToQuotes("['a',{'value0':3,'b':{'value':4}}]"); + JsonParser p0 = JSON_F.createParser(jsonString); + FilteringParserDelegate p = new FilteringParserDelegate(p0, + new NoObjectsFilter(), + Inclusion.INCLUDE_NON_NULL, + true // multipleMatches + ); + String result = readAndWrite(JSON_F, p); + assertEquals(aposToQuotes("['a']"), result); + assertEquals(1, p.getMatchCount()); + } + public void testIndexMatchWithPath1() throws Exception { FilteringParserDelegate p = new FilteringParserDelegate(JSON_F.createParser(SIMPLE), - new IndexMatchFilter(1), true, true); + new IndexMatchFilter(1), Inclusion.INCLUDE_ALL_AND_PATH, true); String result = readAndWrite(JSON_F, p); assertEquals(aposToQuotes("{'array':[2]}"), result); assertEquals(1, p.getMatchCount()); p = new FilteringParserDelegate(JSON_F.createParser(SIMPLE), - new IndexMatchFilter(0), true, true); + new IndexMatchFilter(0), Inclusion.INCLUDE_ALL_AND_PATH, true); result = readAndWrite(JSON_F, p); assertEquals(aposToQuotes("{'array':[1]}"), result); assertEquals(1, p.getMatchCount()); @@ -316,13 +469,13 @@ public void testIndexMatchWithPath1() throws Exception public void testIndexMatchWithPath2() throws Exception { FilteringParserDelegate p = new FilteringParserDelegate(JSON_F.createParser(SIMPLE), - new IndexMatchFilter(0, 1), true, true); + new IndexMatchFilter(0, 1), Inclusion.INCLUDE_ALL_AND_PATH, true); assertEquals(aposToQuotes("{'array':[1,2]}"), readAndWrite(JSON_F, p)); assertEquals(2, p.getMatchCount()); String JSON = aposToQuotes("{'a':123,'array':[1,2,3,4,5],'b':[1,2,3]}"); p = new FilteringParserDelegate(JSON_F.createParser(JSON), - new IndexMatchFilter(1, 3), true, true); + new IndexMatchFilter(1, 3), Inclusion.INCLUDE_ALL_AND_PATH, true); assertEquals(aposToQuotes("{'array':[2,4],'b':[2]}"), readAndWrite(JSON_F, p)); assertEquals(3, p.getMatchCount()); } @@ -332,7 +485,7 @@ public void testBasicSingleMatchFilteringWithPath() throws Exception JsonParser p0 = JSON_F.createParser(SIMPLE); JsonParser p = new FilteringParserDelegate(p0, new NameMatchFilter("value"), - true, // includePath + Inclusion.INCLUDE_ALL_AND_PATH, false // multipleMatches ); @@ -346,7 +499,7 @@ public void testTokensSingleMatchWithPath() throws Exception JsonParser p0 = JSON_F.createParser(SIMPLE); JsonParser p = new FilteringParserDelegate(p0, new NameMatchFilter("value"), - true, // includePath + Inclusion.INCLUDE_ALL_AND_PATH, false // multipleMatches ); @@ -423,7 +576,7 @@ public void testSkippingForSingleWithPath() throws Exception JsonParser p0 = JSON_F.createParser(SIMPLE); JsonParser p = new FilteringParserDelegate(p0, new NameMatchFilter("value"), - true, // includePath + Inclusion.INCLUDE_ALL_AND_PATH, false // multipleMatches ); diff --git a/src/test/java/com/fasterxml/jackson/core/filter/JsonPointerGeneratorFilteringTest.java b/src/test/java/com/fasterxml/jackson/core/filter/JsonPointerGeneratorFilteringTest.java index 245a9806e0..9fcac6c597 100644 --- a/src/test/java/com/fasterxml/jackson/core/filter/JsonPointerGeneratorFilteringTest.java +++ b/src/test/java/com/fasterxml/jackson/core/filter/JsonPointerGeneratorFilteringTest.java @@ -3,6 +3,7 @@ import java.io.*; import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.filter.TokenFilter.Inclusion; @SuppressWarnings("resource") public class JsonPointerGeneratorFilteringTest extends com.fasterxml.jackson.core.BaseTest @@ -13,82 +14,82 @@ public class JsonPointerGeneratorFilteringTest extends com.fasterxml.jackson.cor public void testSimplePropertyWithPath() throws Exception { - _assert(SIMPLE_INPUT, "/c", true, "{'c':{'d':{'a':true}}}"); - _assert(SIMPLE_INPUT, "/c/d", true, "{'c':{'d':{'a':true}}}"); - _assert(SIMPLE_INPUT, "/c/d/a", true, "{'c':{'d':{'a':true}}}"); + _assert(SIMPLE_INPUT, "/c", Inclusion.INCLUDE_ALL_AND_PATH, "{'c':{'d':{'a':true}}}"); + _assert(SIMPLE_INPUT, "/c/d", Inclusion.INCLUDE_ALL_AND_PATH, "{'c':{'d':{'a':true}}}"); + _assert(SIMPLE_INPUT, "/c/d/a", Inclusion.INCLUDE_ALL_AND_PATH, "{'c':{'d':{'a':true}}}"); - _assert(SIMPLE_INPUT, "/c/d/a", true, "{'c':{'d':{'a':true}}}"); + _assert(SIMPLE_INPUT, "/c/d/a", Inclusion.INCLUDE_ALL_AND_PATH, "{'c':{'d':{'a':true}}}"); - _assert(SIMPLE_INPUT, "/a", true, "{'a':1}"); - _assert(SIMPLE_INPUT, "/d", true, "{'d':null}"); + _assert(SIMPLE_INPUT, "/a", Inclusion.INCLUDE_ALL_AND_PATH, "{'a':1}"); + _assert(SIMPLE_INPUT, "/d", Inclusion.INCLUDE_ALL_AND_PATH, "{'d':null}"); // and then non-match - _assert(SIMPLE_INPUT, "/x", true, ""); + _assert(SIMPLE_INPUT, "/x", Inclusion.INCLUDE_ALL_AND_PATH, ""); } public void testSimplePropertyWithoutPath() throws Exception { - _assert(SIMPLE_INPUT, "/c", false, "{'d':{'a':true}}"); - _assert(SIMPLE_INPUT, "/c/d", false, "{'a':true}"); - _assert(SIMPLE_INPUT, "/c/d/a", false, "true"); + _assert(SIMPLE_INPUT, "/c", Inclusion.ONLY_INCLUDE_ALL, "{'d':{'a':true}}"); + _assert(SIMPLE_INPUT, "/c/d", Inclusion.ONLY_INCLUDE_ALL, "{'a':true}"); + _assert(SIMPLE_INPUT, "/c/d/a", Inclusion.ONLY_INCLUDE_ALL, "true"); - _assert(SIMPLE_INPUT, "/a", false, "1"); - _assert(SIMPLE_INPUT, "/d", false, "null"); + _assert(SIMPLE_INPUT, "/a", Inclusion.ONLY_INCLUDE_ALL, "1"); + _assert(SIMPLE_INPUT, "/d", Inclusion.ONLY_INCLUDE_ALL, "null"); // and then non-match - _assert(SIMPLE_INPUT, "/x", false, ""); + _assert(SIMPLE_INPUT, "/x", Inclusion.ONLY_INCLUDE_ALL, ""); } public void testArrayElementWithPath() throws Exception { - _assert(SIMPLE_INPUT, "/b", true, "{'b':[1,2,3]}"); - _assert(SIMPLE_INPUT, "/b/1", true, "{'b':[2]}"); - _assert(SIMPLE_INPUT, "/b/2", true, "{'b':[3]}"); + _assert(SIMPLE_INPUT, "/b", Inclusion.INCLUDE_ALL_AND_PATH, "{'b':[1,2,3]}"); + _assert(SIMPLE_INPUT, "/b/1", Inclusion.INCLUDE_ALL_AND_PATH, "{'b':[2]}"); + _assert(SIMPLE_INPUT, "/b/2", Inclusion.INCLUDE_ALL_AND_PATH, "{'b':[3]}"); // and then non-match - _assert(SIMPLE_INPUT, "/b/8", true, ""); + _assert(SIMPLE_INPUT, "/b/8", Inclusion.INCLUDE_ALL_AND_PATH, ""); } public void testArrayNestedWithPath() throws Exception { - _assert("{'a':[true,{'b':3,'d':2},false]}", "/a/1/b", true, "{'a':[{'b':3}]}"); - _assert("[true,[1]]", "/0", true, "[true]"); - _assert("[true,[1]]", "/1", true, "[[1]]"); - _assert("[true,[1,2,[true],3],0]", "/0", true, "[true]"); - _assert("[true,[1,2,[true],3],0]", "/1", true, "[[1,2,[true],3]]"); - - _assert("[true,[1,2,[true],3],0]", "/1/2", true, "[[[true]]]"); - _assert("[true,[1,2,[true],3],0]", "/1/2/0", true, "[[[true]]]"); - _assert("[true,[1,2,[true],3],0]", "/1/3/0", true, ""); + _assert("{'a':[true,{'b':3,'d':2},false]}", "/a/1/b", Inclusion.INCLUDE_ALL_AND_PATH, "{'a':[{'b':3}]}"); + _assert("[true,[1]]", "/0", Inclusion.INCLUDE_ALL_AND_PATH, "[true]"); + _assert("[true,[1]]", "/1", Inclusion.INCLUDE_ALL_AND_PATH, "[[1]]"); + _assert("[true,[1,2,[true],3],0]", "/0", Inclusion.INCLUDE_ALL_AND_PATH, "[true]"); + _assert("[true,[1,2,[true],3],0]", "/1", Inclusion.INCLUDE_ALL_AND_PATH, "[[1,2,[true],3]]"); + + _assert("[true,[1,2,[true],3],0]", "/1/2", Inclusion.INCLUDE_ALL_AND_PATH, "[[[true]]]"); + _assert("[true,[1,2,[true],3],0]", "/1/2/0", Inclusion.INCLUDE_ALL_AND_PATH, "[[[true]]]"); + _assert("[true,[1,2,[true],3],0]", "/1/3/0", Inclusion.INCLUDE_ALL_AND_PATH, ""); } public void testArrayNestedWithoutPath() throws Exception { - _assert("{'a':[true,{'b':3,'d':2},false]}", "/a/1/b", false, "3"); - _assert("[true,[1,2,[true],3],0]", "/0", false, "true"); - _assert("[true,[1,2,[true],3],0]", "/1", false, + _assert("{'a':[true,{'b':3,'d':2},false]}", "/a/1/b", Inclusion.ONLY_INCLUDE_ALL, "3"); + _assert("[true,[1,2,[true],3],0]", "/0", Inclusion.ONLY_INCLUDE_ALL, "true"); + _assert("[true,[1,2,[true],3],0]", "/1", Inclusion.ONLY_INCLUDE_ALL, "[1,2,[true],3]"); - _assert("[true,[1,2,[true],3],0]", "/1/2", false, "[true]"); - _assert("[true,[1,2,[true],3],0]", "/1/2/0", false, "true"); - _assert("[true,[1,2,[true],3],0]", "/1/3/0", false, ""); + _assert("[true,[1,2,[true],3],0]", "/1/2", Inclusion.ONLY_INCLUDE_ALL, "[true]"); + _assert("[true,[1,2,[true],3],0]", "/1/2/0", Inclusion.ONLY_INCLUDE_ALL, "true"); + _assert("[true,[1,2,[true],3],0]", "/1/3/0", Inclusion.ONLY_INCLUDE_ALL, ""); } // final String SIMPLE_INPUT = aposToQuotes("{'a':1,'b':[1,2,3],'c':{'d':{'a':true}},'d':null}"); public void testArrayElementWithoutPath() throws Exception { - _assert(SIMPLE_INPUT, "/b", false, "[1,2,3]"); - _assert(SIMPLE_INPUT, "/b/1", false, "2"); - _assert(SIMPLE_INPUT, "/b/2", false, "3"); + _assert(SIMPLE_INPUT, "/b", Inclusion.ONLY_INCLUDE_ALL, "[1,2,3]"); + _assert(SIMPLE_INPUT, "/b/1", Inclusion.ONLY_INCLUDE_ALL, "2"); + _assert(SIMPLE_INPUT, "/b/2", Inclusion.ONLY_INCLUDE_ALL, "3"); - _assert(SIMPLE_INPUT, "/b/8", false, ""); + _assert(SIMPLE_INPUT, "/b/8", Inclusion.ONLY_INCLUDE_ALL, ""); // and then non-match - _assert(SIMPLE_INPUT, "/x", false, ""); + _assert(SIMPLE_INPUT, "/x", Inclusion.ONLY_INCLUDE_ALL, ""); } - private void _assert(String input, String pathExpr, boolean includeParent, String exp) + private void _assert(String input, String pathExpr, Inclusion tokenFilterInclusion, String exp) throws Exception { StringWriter w = new StringWriter(); @@ -96,7 +97,7 @@ private void _assert(String input, String pathExpr, boolean includeParent, Strin JsonGenerator g0 = JSON_F.createGenerator(w); FilteringGeneratorDelegate g = new FilteringGeneratorDelegate(g0, new JsonPointerBasedFilter(pathExpr), - includeParent, false); + tokenFilterInclusion, false); try { writeJsonDoc(JSON_F, input, g); diff --git a/src/test/java/com/fasterxml/jackson/core/filter/JsonPointerParserFilteringTest.java b/src/test/java/com/fasterxml/jackson/core/filter/JsonPointerParserFilteringTest.java index 58ecca795d..86fa975902 100644 --- a/src/test/java/com/fasterxml/jackson/core/filter/JsonPointerParserFilteringTest.java +++ b/src/test/java/com/fasterxml/jackson/core/filter/JsonPointerParserFilteringTest.java @@ -3,6 +3,7 @@ import java.io.StringWriter; import com.fasterxml.jackson.core.*; +import com.fasterxml.jackson.core.filter.TokenFilter.Inclusion; public class JsonPointerParserFilteringTest extends com.fasterxml.jackson.core.BaseTest { @@ -14,55 +15,55 @@ public class JsonPointerParserFilteringTest extends com.fasterxml.jackson.core.B public void testSimplestWithPath() throws Exception { - _assert(SIMPLEST_INPUT, "/a", true, "{'a':1}"); - _assert(SIMPLEST_INPUT, "/b", true, "{'b':2}"); - _assert(SIMPLEST_INPUT, "/c", true, "{'c':3}"); - _assert(SIMPLEST_INPUT, "/c/0", true, ""); - _assert(SIMPLEST_INPUT, "/d", true, ""); + _assert(SIMPLEST_INPUT, "/a", Inclusion.INCLUDE_ALL_AND_PATH, "{'a':1}"); + _assert(SIMPLEST_INPUT, "/b", Inclusion.INCLUDE_ALL_AND_PATH, "{'b':2}"); + _assert(SIMPLEST_INPUT, "/c", Inclusion.INCLUDE_ALL_AND_PATH, "{'c':3}"); + _assert(SIMPLEST_INPUT, "/c/0", Inclusion.INCLUDE_ALL_AND_PATH, ""); + _assert(SIMPLEST_INPUT, "/d", Inclusion.INCLUDE_ALL_AND_PATH, ""); } public void testSimplestNoPath() throws Exception { - _assert(SIMPLEST_INPUT, "/a", false, "1"); - _assert(SIMPLEST_INPUT, "/b", false, "2"); - _assert(SIMPLEST_INPUT, "/b/2", false, ""); - _assert(SIMPLEST_INPUT, "/c", false, "3"); - _assert(SIMPLEST_INPUT, "/d", false, ""); + _assert(SIMPLEST_INPUT, "/a", Inclusion.ONLY_INCLUDE_ALL, "1"); + _assert(SIMPLEST_INPUT, "/b", Inclusion.ONLY_INCLUDE_ALL, "2"); + _assert(SIMPLEST_INPUT, "/b/2", Inclusion.ONLY_INCLUDE_ALL, ""); + _assert(SIMPLEST_INPUT, "/c", Inclusion.ONLY_INCLUDE_ALL, "3"); + _assert(SIMPLEST_INPUT, "/d", Inclusion.ONLY_INCLUDE_ALL, ""); } public void testSimpleWithPath() throws Exception { - _assert(SIMPLE_INPUT, "/c", true, "{'c':{'d':{'a':true}}}"); - _assert(SIMPLE_INPUT, "/c/d", true, "{'c':{'d':{'a':true}}}"); - _assert(SIMPLE_INPUT, "/a", true, "{'a':1}"); - _assert(SIMPLE_INPUT, "/b", true, "{'b':[1,2,3]}"); - _assert(SIMPLE_INPUT, "/b/0", true, "{'b':[1]}"); - _assert(SIMPLE_INPUT, "/b/1", true, "{'b':[2]}"); - _assert(SIMPLE_INPUT, "/b/2", true, "{'b':[3]}"); - _assert(SIMPLE_INPUT, "/b/3", true, ""); + _assert(SIMPLE_INPUT, "/c", Inclusion.INCLUDE_ALL_AND_PATH, "{'c':{'d':{'a':true}}}"); + _assert(SIMPLE_INPUT, "/c/d", Inclusion.INCLUDE_ALL_AND_PATH, "{'c':{'d':{'a':true}}}"); + _assert(SIMPLE_INPUT, "/a", Inclusion.INCLUDE_ALL_AND_PATH, "{'a':1}"); + _assert(SIMPLE_INPUT, "/b", Inclusion.INCLUDE_ALL_AND_PATH, "{'b':[1,2,3]}"); + _assert(SIMPLE_INPUT, "/b/0", Inclusion.INCLUDE_ALL_AND_PATH, "{'b':[1]}"); + _assert(SIMPLE_INPUT, "/b/1", Inclusion.INCLUDE_ALL_AND_PATH, "{'b':[2]}"); + _assert(SIMPLE_INPUT, "/b/2", Inclusion.INCLUDE_ALL_AND_PATH, "{'b':[3]}"); + _assert(SIMPLE_INPUT, "/b/3", Inclusion.INCLUDE_ALL_AND_PATH, ""); } public void testSimpleNoPath() throws Exception { - _assert(SIMPLE_INPUT, "/c", false, "{'d':{'a':true}}"); + _assert(SIMPLE_INPUT, "/c", Inclusion.ONLY_INCLUDE_ALL, "{'d':{'a':true}}"); - _assert(SIMPLE_INPUT, "/c/d", false, "{'a':true}"); - _assert(SIMPLE_INPUT, "/a", false, "1"); - _assert(SIMPLE_INPUT, "/b", false, "[1,2,3]"); - _assert(SIMPLE_INPUT, "/b/0", false, "1"); - _assert(SIMPLE_INPUT, "/b/1", false, "2"); - _assert(SIMPLE_INPUT, "/b/2", false, "3"); - _assert(SIMPLE_INPUT, "/b/3", false, ""); + _assert(SIMPLE_INPUT, "/c/d", Inclusion.ONLY_INCLUDE_ALL, "{'a':true}"); + _assert(SIMPLE_INPUT, "/a", Inclusion.ONLY_INCLUDE_ALL, "1"); + _assert(SIMPLE_INPUT, "/b", Inclusion.ONLY_INCLUDE_ALL, "[1,2,3]"); + _assert(SIMPLE_INPUT, "/b/0", Inclusion.ONLY_INCLUDE_ALL, "1"); + _assert(SIMPLE_INPUT, "/b/1", Inclusion.ONLY_INCLUDE_ALL, "2"); + _assert(SIMPLE_INPUT, "/b/2", Inclusion.ONLY_INCLUDE_ALL, "3"); + _assert(SIMPLE_INPUT, "/b/3", Inclusion.ONLY_INCLUDE_ALL, ""); } @SuppressWarnings("resource") - void _assert(String input, String pathExpr, boolean includeParent, String exp) + void _assert(String input, String pathExpr, TokenFilter.Inclusion inclusion, String exp) throws Exception { JsonParser p0 = JSON_F.createParser(input); FilteringParserDelegate p = new FilteringParserDelegate(p0, new JsonPointerBasedFilter(pathExpr), - includeParent, false); + inclusion, false); StringWriter w = new StringWriter(); JsonGenerator g = JSON_F.createGenerator(w); diff --git a/src/test/java/com/fasterxml/jackson/core/json/async/AsyncTokenFilterTest.java b/src/test/java/com/fasterxml/jackson/core/json/async/AsyncTokenFilterTest.java index ded3a27cc6..33e77a194d 100644 --- a/src/test/java/com/fasterxml/jackson/core/json/async/AsyncTokenFilterTest.java +++ b/src/test/java/com/fasterxml/jackson/core/json/async/AsyncTokenFilterTest.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.core.async.AsyncTestBase; import com.fasterxml.jackson.core.filter.FilteringParserDelegate; import com.fasterxml.jackson.core.filter.TokenFilter; +import com.fasterxml.jackson.core.filter.TokenFilter.Inclusion; // [core#462], [core#463] public class AsyncTokenFilterTest extends AsyncTestBase @@ -33,7 +34,7 @@ public void testFilteredNonBlockingParserAllContent() throws IOException { NonBlockingJsonParser nonBlockingParser = (NonBlockingJsonParser) JSON_F.createNonBlockingByteArrayParser(); FilteringParserDelegate filteredParser = new FilteringParserDelegate(nonBlockingParser, - TOKEN_FILTER, true, true); + TOKEN_FILTER, Inclusion.INCLUDE_ALL_AND_PATH, true); nonBlockingParser.feedInput(INPUT_BYTES, 0, INPUT_BYTES.length); int expectedIdx = 0; while (expectedIdx < EXPECTED_TOKENS.length) { @@ -54,7 +55,7 @@ public void testSkipChildrenFailOnSplit() throws IOException NonBlockingJsonParser nbParser = (NonBlockingJsonParser) JSON_F.createNonBlockingByteArrayParser(); @SuppressWarnings("resource") FilteringParserDelegate filteredParser = new FilteringParserDelegate(nbParser, - TOKEN_FILTER, true, true); + TOKEN_FILTER, Inclusion.INCLUDE_ALL_AND_PATH, true); nbParser.feedInput(INPUT_BYTES, 0, 5); assertToken(JsonToken.START_OBJECT, nbParser.nextToken()); diff --git a/src/test/java/com/fasterxml/jackson/core/write/UTF8GeneratorTest.java b/src/test/java/com/fasterxml/jackson/core/write/UTF8GeneratorTest.java index 7cd399af4b..f8f20f4d5e 100644 --- a/src/test/java/com/fasterxml/jackson/core/write/UTF8GeneratorTest.java +++ b/src/test/java/com/fasterxml/jackson/core/write/UTF8GeneratorTest.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.core.filter.FilteringGeneratorDelegate; import com.fasterxml.jackson.core.filter.JsonPointerBasedFilter; +import com.fasterxml.jackson.core.filter.TokenFilter.Inclusion; import com.fasterxml.jackson.core.io.IOContext; import com.fasterxml.jackson.core.json.UTF8JsonGenerator; import com.fasterxml.jackson.core.util.BufferRecycler; @@ -74,7 +75,7 @@ public void testFilteringWithEscapedChars() throws Exception FilteringGeneratorDelegate gen = new FilteringGeneratorDelegate(g, new JsonPointerBasedFilter("/escapes"), - true, // includePath + Inclusion.INCLUDE_ALL_AND_PATH, false // multipleMatches );