private static <V> void parseFeature(
            JsonParser parser, ValueParser<V> valueParser, FeatureHandler<V> featureHandler) throws IOException {
        String key = null;
        V value = null;
        long subqueryBitmap = SubqueryBitmap.DEFAULT_VALUE; // Specifying subquery bitmap is optional.

        while (parser.nextToken() != JsonToken.END_OBJECT) {
            String fieldName = parser.currentName();
            skipToken(parser, JsonToken.VALUE_STRING, JsonToken.VALUE_NUMBER_INT);
            switch (fieldName) {
                case "k":
                    key = parser.getText();
                    break;
                case "v":
                    value = valueParser.parse(parser);
                    break;
                case "s":
                    subqueryBitmap = fromHexString(parser.getText());
                    break;
                default:
                    throw new IllegalArgumentException("Invalid field name: " + fieldName);
            }
        }
        if (key == null) {
            throw new IllegalArgumentException(
                    String.format("Feature key is missing! (%s)", parser.currentLocation()));
        }
        if (value == null) {
            throw new IllegalArgumentException(
                    String.format("Feature value is missing! (%s)", parser.currentLocation()));
        }
        featureHandler.accept(key, value, subqueryBitmap);
    }
Parses predicate query from JSON.
@param json JSON input.
@param featureHandler The handler is invoked when a feature is parsed.
@param rangeFeatureHandler The handler is invoked when a range feature is parsed.
@throws IllegalArgumentException If JSON is invalid.
predicate-search-core/src/main/java/com/yahoo/search/predicate/PredicateQueryParser.java