mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-02-17 02:14:54 +00:00
Add generic array support to AbstractObjectParser (#28552)
Adds a generic declareFieldArray that can process arrays of arbitrary elements.
This commit is contained in:
parent
f562c7f15a
commit
da1a10fa92
@ -183,27 +183,35 @@ public abstract class AbstractObjectParser<Value, Context>
|
|||||||
|
|
||||||
public <T> void declareObjectArray(BiConsumer<Value, List<T>> consumer, ContextParser<Context, T> objectParser,
|
public <T> void declareObjectArray(BiConsumer<Value, List<T>> consumer, ContextParser<Context, T> objectParser,
|
||||||
ParseField field) {
|
ParseField field) {
|
||||||
declareField(consumer, (p, c) -> parseArray(p, () -> objectParser.parse(p, c)), field, ValueType.OBJECT_ARRAY);
|
declareFieldArray(consumer, (p, c) -> objectParser.parse(p, c), field, ValueType.OBJECT_ARRAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void declareStringArray(BiConsumer<Value, List<String>> consumer, ParseField field) {
|
public void declareStringArray(BiConsumer<Value, List<String>> consumer, ParseField field) {
|
||||||
declareField(consumer, (p, c) -> parseArray(p, p::text), field, ValueType.STRING_ARRAY);
|
declareFieldArray(consumer, (p, c) -> p.text(), field, ValueType.STRING_ARRAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void declareDoubleArray(BiConsumer<Value, List<Double>> consumer, ParseField field) {
|
public void declareDoubleArray(BiConsumer<Value, List<Double>> consumer, ParseField field) {
|
||||||
declareField(consumer, (p, c) -> parseArray(p, p::doubleValue), field, ValueType.DOUBLE_ARRAY);
|
declareFieldArray(consumer, (p, c) -> p.doubleValue(), field, ValueType.DOUBLE_ARRAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void declareFloatArray(BiConsumer<Value, List<Float>> consumer, ParseField field) {
|
public void declareFloatArray(BiConsumer<Value, List<Float>> consumer, ParseField field) {
|
||||||
declareField(consumer, (p, c) -> parseArray(p, p::floatValue), field, ValueType.FLOAT_ARRAY);
|
declareFieldArray(consumer, (p, c) -> p.floatValue(), field, ValueType.FLOAT_ARRAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void declareLongArray(BiConsumer<Value, List<Long>> consumer, ParseField field) {
|
public void declareLongArray(BiConsumer<Value, List<Long>> consumer, ParseField field) {
|
||||||
declareField(consumer, (p, c) -> parseArray(p, p::longValue), field, ValueType.LONG_ARRAY);
|
declareFieldArray(consumer, (p, c) -> p.longValue(), field, ValueType.LONG_ARRAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void declareIntArray(BiConsumer<Value, List<Integer>> consumer, ParseField field) {
|
public void declareIntArray(BiConsumer<Value, List<Integer>> consumer, ParseField field) {
|
||||||
declareField(consumer, (p, c) -> parseArray(p, p::intValue), field, ValueType.INT_ARRAY);
|
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 <T> void declareFieldArray(BiConsumer<Value, List<T>> consumer, ContextParser<Context, T> itemParser,
|
||||||
|
ParseField field, ValueType type) {
|
||||||
|
declareField(consumer, (p, c) -> parseArray(p, () -> itemParser.parse(p, c)), field, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void declareRawObject(BiConsumer<Value, BytesReference> consumer, ParseField field) {
|
public void declareRawObject(BiConsumer<Value, BytesReference> consumer, ParseField field) {
|
||||||
@ -220,13 +228,18 @@ public abstract class AbstractObjectParser<Value, Context>
|
|||||||
private interface IOSupplier<T> {
|
private interface IOSupplier<T> {
|
||||||
T get() throws IOException;
|
T get() throws IOException;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T> List<T> parseArray(XContentParser parser, IOSupplier<T> supplier) throws IOException {
|
private static <T> List<T> parseArray(XContentParser parser, IOSupplier<T> supplier) throws IOException {
|
||||||
List<T> list = new ArrayList<>();
|
List<T> list = new ArrayList<>();
|
||||||
if (parser.currentToken().isValue() || parser.currentToken() == XContentParser.Token.START_OBJECT) {
|
if (parser.currentToken().isValue()
|
||||||
|
|| parser.currentToken() == XContentParser.Token.VALUE_NULL
|
||||||
|
|| parser.currentToken() == XContentParser.Token.START_OBJECT) {
|
||||||
list.add(supplier.get()); // single value
|
list.add(supplier.get()); // single value
|
||||||
} else {
|
} else {
|
||||||
while (parser.nextToken() != XContentParser.Token.END_ARRAY) {
|
while (parser.nextToken() != XContentParser.Token.END_ARRAY) {
|
||||||
if (parser.currentToken().isValue() || parser.currentToken() == XContentParser.Token.START_OBJECT) {
|
if (parser.currentToken().isValue()
|
||||||
|
|| parser.currentToken() == XContentParser.Token.VALUE_NULL
|
||||||
|
|| parser.currentToken() == XContentParser.Token.START_OBJECT) {
|
||||||
list.add(supplier.get());
|
list.add(supplier.get());
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("expected value but got [" + parser.currentToken() + "]");
|
throw new IllegalStateException("expected value but got [" + parser.currentToken() + "]");
|
||||||
|
@ -416,7 +416,8 @@ public final class ObjectParser<Value, Context> extends AbstractObjectParser<Val
|
|||||||
OBJECT_ARRAY_BOOLEAN_OR_STRING(START_OBJECT, START_ARRAY, VALUE_BOOLEAN, VALUE_STRING),
|
OBJECT_ARRAY_BOOLEAN_OR_STRING(START_OBJECT, START_ARRAY, VALUE_BOOLEAN, VALUE_STRING),
|
||||||
OBJECT_ARRAY_OR_STRING(START_OBJECT, START_ARRAY, VALUE_STRING),
|
OBJECT_ARRAY_OR_STRING(START_OBJECT, START_ARRAY, VALUE_STRING),
|
||||||
VALUE(VALUE_BOOLEAN, VALUE_NULL, VALUE_EMBEDDED_OBJECT, VALUE_NUMBER, VALUE_STRING),
|
VALUE(VALUE_BOOLEAN, VALUE_NULL, VALUE_EMBEDDED_OBJECT, VALUE_NUMBER, VALUE_STRING),
|
||||||
VALUE_OBJECT_ARRAY(VALUE_BOOLEAN, VALUE_NULL, VALUE_EMBEDDED_OBJECT, VALUE_NUMBER, VALUE_STRING, START_OBJECT, START_ARRAY);
|
VALUE_OBJECT_ARRAY(VALUE_BOOLEAN, VALUE_NULL, VALUE_EMBEDDED_OBJECT, VALUE_NUMBER, VALUE_STRING, START_OBJECT, START_ARRAY),
|
||||||
|
VALUE_ARRAY(VALUE_BOOLEAN, VALUE_NULL, VALUE_NUMBER, VALUE_STRING, START_ARRAY);
|
||||||
|
|
||||||
private final EnumSet<XContentParser.Token> tokens;
|
private final EnumSet<XContentParser.Token> tokens;
|
||||||
|
|
||||||
|
@ -34,13 +34,14 @@ import java.util.Arrays;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.startsWith;
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.hamcrest.Matchers.hasSize;
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
|
|
||||||
public class ObjectParserTests extends ESTestCase {
|
public class ObjectParserTests extends ESTestCase {
|
||||||
|
|
||||||
public void testBasics() throws IOException {
|
public void testBasics() throws IOException {
|
||||||
XContentParser parser = createParser(JsonXContent.jsonXContent,
|
XContentParser parser = createParser(JsonXContent.jsonXContent,
|
||||||
"{\n"
|
"{\n"
|
||||||
+ " \"test\" : \"foo\",\n"
|
+ " \"test\" : \"foo\",\n"
|
||||||
+ " \"test_number\" : 2,\n"
|
+ " \"test_number\" : 2,\n"
|
||||||
@ -449,7 +450,7 @@ public class ObjectParserTests extends ESTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testParseNamedObject() throws IOException {
|
public void testParseNamedObject() throws IOException {
|
||||||
XContentParser parser = createParser(JsonXContent.jsonXContent,
|
XContentParser parser = createParser(JsonXContent.jsonXContent,
|
||||||
"{\"named\": {\n"
|
"{\"named\": {\n"
|
||||||
+ " \"a\": {}"
|
+ " \"a\": {}"
|
||||||
+ "}}");
|
+ "}}");
|
||||||
@ -460,7 +461,7 @@ public class ObjectParserTests extends ESTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testParseNamedObjectInOrder() throws IOException {
|
public void testParseNamedObjectInOrder() throws IOException {
|
||||||
XContentParser parser = createParser(JsonXContent.jsonXContent,
|
XContentParser parser = createParser(JsonXContent.jsonXContent,
|
||||||
"{\"named\": [\n"
|
"{\"named\": [\n"
|
||||||
+ " {\"a\": {}}"
|
+ " {\"a\": {}}"
|
||||||
+ "]}");
|
+ "]}");
|
||||||
@ -471,7 +472,7 @@ public class ObjectParserTests extends ESTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testParseNamedObjectTwoFieldsInArray() throws IOException {
|
public void testParseNamedObjectTwoFieldsInArray() throws IOException {
|
||||||
XContentParser parser = createParser(JsonXContent.jsonXContent,
|
XContentParser parser = createParser(JsonXContent.jsonXContent,
|
||||||
"{\"named\": [\n"
|
"{\"named\": [\n"
|
||||||
+ " {\"a\": {}, \"b\": {}}"
|
+ " {\"a\": {}, \"b\": {}}"
|
||||||
+ "]}");
|
+ "]}");
|
||||||
@ -483,7 +484,7 @@ public class ObjectParserTests extends ESTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testParseNamedObjectNoFieldsInArray() throws IOException {
|
public void testParseNamedObjectNoFieldsInArray() throws IOException {
|
||||||
XContentParser parser = createParser(JsonXContent.jsonXContent,
|
XContentParser parser = createParser(JsonXContent.jsonXContent,
|
||||||
"{\"named\": [\n"
|
"{\"named\": [\n"
|
||||||
+ " {}"
|
+ " {}"
|
||||||
+ "]}");
|
+ "]}");
|
||||||
@ -495,7 +496,7 @@ public class ObjectParserTests extends ESTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testParseNamedObjectJunkInArray() throws IOException {
|
public void testParseNamedObjectJunkInArray() throws IOException {
|
||||||
XContentParser parser = createParser(JsonXContent.jsonXContent,
|
XContentParser parser = createParser(JsonXContent.jsonXContent,
|
||||||
"{\"named\": [\n"
|
"{\"named\": [\n"
|
||||||
+ " \"junk\""
|
+ " \"junk\""
|
||||||
+ "]}");
|
+ "]}");
|
||||||
@ -594,6 +595,59 @@ public class ObjectParserTests extends ESTestCase {
|
|||||||
assertEquals(s.test, "foo");
|
assertEquals(s.test, "foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testArraysOfGenericValues() throws IOException {
|
||||||
|
XContentParser parser = createParser(JsonXContent.jsonXContent,
|
||||||
|
"{\n"
|
||||||
|
+ " \"test_array\": [ 1, null, \"3\", 4.2],\n"
|
||||||
|
+ " \"int_array\": [ 1, 2, 3]\n"
|
||||||
|
+ "}");
|
||||||
|
class TestStruct {
|
||||||
|
List<Object> testArray = new ArrayList<>();
|
||||||
|
|
||||||
|
List<Integer> ints = new ArrayList<>();
|
||||||
|
|
||||||
|
public void setInts(List<Integer> ints) {
|
||||||
|
this.ints = ints;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setArray(List<Object> testArray) {
|
||||||
|
this.testArray = testArray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ObjectParser<TestStruct, Void> objectParser = new ObjectParser<>("foo");
|
||||||
|
TestStruct s = new TestStruct();
|
||||||
|
|
||||||
|
objectParser.declareFieldArray(TestStruct::setArray, (p, c) -> XContentParserUtils.parseFieldsValue(p),
|
||||||
|
new ParseField("test_array"), ValueType.VALUE_ARRAY);
|
||||||
|
objectParser.declareIntArray(TestStruct::setInts, new ParseField("int_array"));
|
||||||
|
objectParser.parse(parser, s, null);
|
||||||
|
assertEquals(s.testArray, Arrays.asList(1, null, "3", 4.2));
|
||||||
|
assertEquals(s.ints, Arrays.asList(1, 2, 3));
|
||||||
|
|
||||||
|
parser = createParser(JsonXContent.jsonXContent, "{\"test_array\": 42}");
|
||||||
|
s = new TestStruct();
|
||||||
|
objectParser.parse(parser, s, null);
|
||||||
|
assertEquals(s.testArray, Collections.singletonList(42));
|
||||||
|
|
||||||
|
parser = createParser(JsonXContent.jsonXContent, "{\"test_array\": [null]}");
|
||||||
|
s = new TestStruct();
|
||||||
|
objectParser.parse(parser, s, null);
|
||||||
|
assertThat(s.testArray, hasSize(1));
|
||||||
|
assertNull(s.testArray.get(0));
|
||||||
|
|
||||||
|
parser = createParser(JsonXContent.jsonXContent, "{\"test_array\": null}");
|
||||||
|
s = new TestStruct();
|
||||||
|
objectParser.parse(parser, s, null);
|
||||||
|
assertThat(s.testArray, hasSize(1));
|
||||||
|
assertNull(s.testArray.get(0));
|
||||||
|
|
||||||
|
// Make sure that we didn't break the null handling in arrays that shouldn't support nulls
|
||||||
|
XContentParser parser2 = createParser(JsonXContent.jsonXContent, "{\"int_array\": [1, null, 3]}");
|
||||||
|
TestStruct s2 = new TestStruct();
|
||||||
|
ParsingException ex = expectThrows(ParsingException.class, () -> objectParser.parse(parser2, s2, null));
|
||||||
|
assertThat(ex.getMessage(), startsWith("[foo] failed to parse field [int_array]"));
|
||||||
|
}
|
||||||
|
|
||||||
static class NamedObjectHolder {
|
static class NamedObjectHolder {
|
||||||
public static final ObjectParser<NamedObjectHolder, Void> PARSER = new ObjectParser<>("named_object_holder",
|
public static final ObjectParser<NamedObjectHolder, Void> PARSER = new ObjectParser<>("named_object_holder",
|
||||||
NamedObjectHolder::new);
|
NamedObjectHolder::new);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user