All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.elasticsearch.xcontent.AbstractObjectParser Maven / Gradle / Ivy

There is a newer version: 8.16.0
Show newest version
/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the Elastic License
 * 2.0 and the Server Side Public License, v 1; you may not use this file except
 * in compliance with, at your election, the Elastic License 2.0 or the Server
 * Side Public License, v 1.
 */

package org.elasticsearch.xcontent;

import org.elasticsearch.core.CheckedFunction;
import org.elasticsearch.xcontent.ObjectParser.NamedObjectParser;
import org.elasticsearch.xcontent.ObjectParser.ValueType;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * Superclass for {@link ObjectParser} and {@link ConstructingObjectParser}. Defines most of the "declare" methods so they can be shared.
 */
public abstract class AbstractObjectParser {

    /**
     * Declare some field. Usually it is easier to use {@link #declareString(BiConsumer, ParseField)} or
     * {@link #declareObject(BiConsumer, ContextParser, ParseField)} rather than call this directly.
     */
    public abstract  void declareField(
        BiConsumer consumer,
        ContextParser parser,
        ParseField parseField,
        ValueType type
    );

    /**
     * Declares a single named object.
     *
     * 
     * 
     * {
     *   "object_name": {
     *     "instance_name": { "field1": "value1", ... }
     *     }
     *   }
     * }
     * 
     * 
* * @param consumer * sets the value once it has been parsed * @param namedObjectParser * parses the named object * @param parseField * the field to parse */ public abstract void declareNamedObject( BiConsumer consumer, NamedObjectParser namedObjectParser, ParseField parseField ); /** * Declares named objects in the style of aggregations. These are named * inside and object like this: * *
     * 
     * {
     *   "aggregations": {
     *     "name_1": { "aggregation_type": {} },
     *     "name_2": { "aggregation_type": {} },
     *     "name_3": { "aggregation_type": {} }
     *     }
     *   }
     * }
     * 
     * 
* * Unlike the other version of this method, "ordered" mode (arrays of * objects) is not supported. * * See NamedObjectHolder in ObjectParserTests for examples of how to invoke * this. * * @param consumer * sets the values once they have been parsed * @param namedObjectParser * parses each named object * @param parseField * the field to parse */ public abstract void declareNamedObjects( BiConsumer> consumer, NamedObjectParser namedObjectParser, ParseField parseField ); /** * Declares named objects in the style of highlighting's field element. * These are usually named inside and object like this: * *
     * 
     * {
     *   "highlight": {
     *     "fields": {        <------ this one
     *       "title": {},
     *       "body": {},
     *       "category": {}
     *     }
     *   }
     * }
     * 
     * 
* * but, when order is important, some may be written this way: * *
     * 
     * {
     *   "highlight": {
     *     "fields": [        <------ this one
     *       {"title": {}},
     *       {"body": {}},
     *       {"category": {}}
     *     ]
     *   }
     * }
     * 
     * 
* * This is because json doesn't enforce ordering. Elasticsearch reads it in * the order sent but tools that generate json are free to put object * members in an unordered Map, jumbling them. Thus, if you care about order * you can send the object in the second way. * * See NamedObjectHolder in ObjectParserTests for examples of how to invoke * this. * * @param consumer * sets the values once they have been parsed * @param namedObjectParser * parses each named object * @param orderedModeCallback * called when the named object is parsed using the "ordered" * mode (the array of objects) * @param parseField * the field to parse */ public abstract void declareNamedObjects( BiConsumer> consumer, NamedObjectParser namedObjectParser, Consumer orderedModeCallback, ParseField parseField ); public abstract String getName(); public void declareField( BiConsumer consumer, CheckedFunction parser, ParseField parseField, ValueType type ) { if (parser == null) { throw new IllegalArgumentException("[parser] is required"); } declareField(consumer, (p, c) -> parser.apply(p), parseField, type); } public void declareObject(BiConsumer consumer, ContextParser objectParser, ParseField field) { declareField(consumer, objectParser, field, ValueType.OBJECT); } /** * Declare an object field that parses explicit {@code null}s in the json to a default value. */ public void declareObjectOrNull( BiConsumer consumer, ContextParser objectParser, T nullValue, ParseField field ) { declareField( consumer, (p, c) -> p.currentToken() == XContentParser.Token.VALUE_NULL ? nullValue : objectParser.parse(p, c), field, ValueType.OBJECT_OR_NULL ); } public void declareFloat(BiConsumer consumer, ParseField field) { // Using a method reference here angers some compilers declareField(consumer, p -> p.floatValue(), field, ValueType.FLOAT); } /** * Declare a float field that parses explicit {@code null}s in the json to a default value. */ public void declareFloatOrNull(BiConsumer consumer, float nullValue, ParseField field) { declareField( consumer, p -> p.currentToken() == XContentParser.Token.VALUE_NULL ? nullValue : p.floatValue(), field, ValueType.FLOAT_OR_NULL ); } public void declareDouble(BiConsumer consumer, ParseField field) { // Using a method reference here angers some compilers declareField(consumer, p -> p.doubleValue(), field, ValueType.DOUBLE); } /** * Declare a double field that parses explicit {@code null}s in the json to a default value. */ public void declareDoubleOrNull(BiConsumer consumer, double nullValue, ParseField field) { declareField( consumer, p -> p.currentToken() == XContentParser.Token.VALUE_NULL ? nullValue : p.doubleValue(), field, ValueType.DOUBLE_OR_NULL ); } public void declareLong(BiConsumer consumer, ParseField field) { // Using a method reference here angers some compilers declareField(consumer, p -> p.longValue(), field, ValueType.LONG); } public void declareLongOrNull(BiConsumer consumer, long nullValue, ParseField field) { // Using a method reference here angers some compilers declareField( consumer, p -> p.currentToken() == XContentParser.Token.VALUE_NULL ? nullValue : p.longValue(), field, ValueType.LONG_OR_NULL ); } public void declareInt(BiConsumer consumer, ParseField field) { // Using a method reference here angers some compilers declareField(consumer, p -> p.intValue(), field, ValueType.INT); } /** * Declare a double field that parses explicit {@code null}s in the json to a default value. */ public void declareIntOrNull(BiConsumer consumer, int nullValue, ParseField field) { declareField( consumer, p -> p.currentToken() == XContentParser.Token.VALUE_NULL ? nullValue : p.intValue(), field, ValueType.INT_OR_NULL ); } public void declareString(BiConsumer consumer, ParseField field) { declareField(consumer, XContentParser::text, field, ValueType.STRING); } /** * Declare a field of type {@code T} parsed from string and converted to {@code T} using provided function. * Throws if the next token is not a string. */ public void declareString(BiConsumer consumer, Function fromStringFunction, ParseField field) { declareField(consumer, p -> fromStringFunction.apply(p.text()), field, ValueType.STRING); } public void declareStringOrNull(BiConsumer consumer, ParseField field) { declareField( consumer, (p) -> p.currentToken() == XContentParser.Token.VALUE_NULL ? null : p.text(), field, ValueType.STRING_OR_NULL ); } public void declareBoolean(BiConsumer consumer, ParseField field) { declareField(consumer, XContentParser::booleanValue, field, ValueType.BOOLEAN); } public void declareObjectArray(BiConsumer> consumer, ContextParser objectParser, ParseField field) { declareFieldArray(consumer, objectParser, field, ValueType.OBJECT_ARRAY); } /** * like {@link #declareObjectArray(BiConsumer, ContextParser, ParseField)}, but can also handle single null values, * in which case the consumer isn't called */ public void declareObjectArrayOrNull( BiConsumer> consumer, ContextParser objectParser, ParseField field ) { declareField( (value, list) -> { if (list != null) consumer.accept(value, list); }, (p, c) -> p.currentToken() == XContentParser.Token.VALUE_NULL ? null : parseArray(p, () -> objectParser.parse(p, c)), field, ValueType.OBJECT_ARRAY_OR_NULL ); } public void declareStringArray(BiConsumer> consumer, ParseField field) { declareFieldArray(consumer, (p, c) -> p.text(), field, ValueType.STRING_ARRAY); } public void declareDoubleArray(BiConsumer> consumer, ParseField field) { declareFieldArray(consumer, (p, c) -> p.doubleValue(), field, ValueType.DOUBLE_ARRAY); } public void declareFloatArray(BiConsumer> consumer, ParseField field) { declareFieldArray(consumer, (p, c) -> p.floatValue(), field, ValueType.FLOAT_ARRAY); } public void declareLongArray(BiConsumer> consumer, ParseField field) { declareFieldArray(consumer, (p, c) -> p.longValue(), field, ValueType.LONG_ARRAY); } public void declareIntArray(BiConsumer> consumer, ParseField field) { declareFieldArray(consumer, (p, c) -> p.intValue(), field, ValueType.INT_ARRAY); } /** * Declares a field that can contain an array of elements listed in the type ValueType enum */ public void declareFieldArray( BiConsumer> consumer, ContextParser itemParser, ParseField field, ValueType type ) { declareField(consumer, (p, c) -> parseArray(p, () -> itemParser.parse(p, c)), field, type); } /** * Declares a set of fields that are required for parsing to succeed. Only one of the values * provided per String[] must be matched. * * E.g. declareRequiredFieldSet("foo", "bar"); means at least one of "foo" or * "bar" fields must be present. If neither of those fields are present, an exception will be thrown. * * Multiple required sets can be configured: * *

     *   parser.declareRequiredFieldSet("foo", "bar");
     *   parser.declareRequiredFieldSet("bizz", "buzz");
     * 
* * requires that one of "foo" or "bar" fields are present, and also that one of "bizz" or * "buzz" fields are present. * * In JSON, it means any of these combinations are acceptable: * *
    *
  • {"foo":"...", "bizz": "..."}
  • *
  • {"bar":"...", "bizz": "..."}
  • *
  • {"foo":"...", "buzz": "..."}
  • *
  • {"bar":"...", "buzz": "..."}
  • *
  • {"foo":"...", "bar":"...", "bizz": "..."}
  • *
  • {"foo":"...", "bar":"...", "buzz": "..."}
  • *
  • {"foo":"...", "bizz":"...", "buzz": "..."}
  • *
  • {"bar":"...", "bizz":"...", "buzz": "..."}
  • *
  • {"foo":"...", "bar":"...", "bizz": "...", "buzz": "..."}
  • *
* * The following would however be rejected: * * * * * * * * * * * *
failure cases
Provided JSONReason for failure
{"foo":"..."}Missing "bizz" or "buzz" field
{"bar":"..."}Missing "bizz" or "buzz" field
{"bizz": "..."}Missing "foo" or "bar" field
{"buzz": "..."}Missing "foo" or "bar" field
{"foo":"...", "bar": "..."}Missing "bizz" or "buzz" field
{"bizz":"...", "buzz": "..."}Missing "foo" or "bar" field
{"unrelated":"..."} Missing "foo" or "bar" field, and missing "bizz" or "buzz" field
* * @param requiredSet * A set of required fields, where at least one of the fields in the array _must_ be present */ public abstract void declareRequiredFieldSet(String... requiredSet); /** * Declares a set of fields of which at most one must appear for parsing to succeed * * E.g. declareExclusiveFieldSet("foo", "bar"); means that only one of 'foo' * or 'bar' must be present, and if both appear then an exception will be thrown. Note * that this does not make 'foo' or 'bar' required - see {@link #declareRequiredFieldSet(String...)} * for required fields. * * Multiple exclusive sets may be declared * * @param exclusiveSet a set of field names, at most one of which must appear */ public abstract void declareExclusiveFieldSet(String... exclusiveSet); private interface IOSupplier { T get() throws IOException; } private static List parseArray(XContentParser parser, IOSupplier supplier) throws IOException { List list = new ArrayList<>(); if (parser.currentToken().isValue() || parser.currentToken() == XContentParser.Token.VALUE_NULL || parser.currentToken() == XContentParser.Token.START_OBJECT) { list.add(supplier.get()); // single value } else { while (parser.nextToken() != XContentParser.Token.END_ARRAY) { if (parser.currentToken().isValue() || parser.currentToken() == XContentParser.Token.VALUE_NULL || parser.currentToken() == XContentParser.Token.START_OBJECT) { list.add(supplier.get()); } else { throw new IllegalStateException("expected value but got [" + parser.currentToken() + "]"); } } } return list; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy