in processing/src/main/java/org/apache/druid/segment/nested/NestedPathFinder.java [109:199]
public static List<NestedPathPart> parseJsonPath(@Nullable String path)
{
if (path == null || path.isEmpty()) {
return Collections.emptyList();
}
List<NestedPathPart> parts = new ArrayList<>();
if (!path.startsWith(JSON_PATH_ROOT)) {
badFormatJsonPath(path, "it must start with '$'");
}
if (path.length() == 1) {
return Collections.emptyList();
}
int partMark = -1; // position to start the next substring to build the path part
int dotMark = -1; // last position where a '.' character was encountered, indicating the start of a new part
int arrayMark = -1; // position of leading '[' indicating start of array (or field name if ' immediately follows)
int quoteMark = -1; // position of leading ', indicating a quoted field name
// start at position 1 since $ is special
for (int i = 1; i < path.length(); i++) {
final char current = path.charAt(i);
if (current == '.' && arrayMark < 0 && quoteMark < 0) {
if (dotMark >= 0) {
parts.add(new NestedPathField(getPathSubstring(path, partMark, i)));
}
dotMark = i;
partMark = i + 1;
} else if (current == '[' && arrayMark < 0 && quoteMark < 0) {
if (dotMark == (i - 1) && dotMark != 0) {
badFormatJsonPath(path, "found '[' at invalid position [%s], must not follow '.' or must be contained with '", i);
}
if (dotMark >= 0 && i > 1) {
parts.add(new NestedPathField(getPathSubstring(path, partMark, i)));
dotMark = -1;
}
arrayMark = i;
partMark = i + 1;
} else if (current == ']' && arrayMark >= 0 && quoteMark < 0) {
String maybeNumber = getPathSubstring(path, partMark, i);
try {
int index = Integer.parseInt(maybeNumber);
parts.add(new NestedPathArrayElement(index));
dotMark = -1;
arrayMark = -1;
partMark = i + 1;
}
catch (NumberFormatException ignored) {
badFormatJsonPath(path, "array specifier [%s] should be a number, it was not. Use ' if this value was meant to be a field name", maybeNumber);
}
} else if (dotMark == -1 && arrayMark == -1) {
badFormatJsonPath(path, "path parts must be separated with '.'");
} else if (current == '\'' && quoteMark < 0) {
if (arrayMark != i - 1) {
badFormatJsonPath(path, "single-quote (') must be immediately after '['");
}
quoteMark = i;
partMark = i + 1;
} else if (current == '\'' && quoteMark >= 0 && path.charAt(i - 1) != '\\') {
if (path.charAt(i + 1) != ']') {
if (arrayMark >= 0) {
continue;
}
badFormatJsonPath(path, "closing single-quote (') must immediately precede ']'");
}
parts.add(new NestedPathField(getPathSubstring(path, partMark, i)));
dotMark = -1;
quoteMark = -1;
// chomp to next char to eat closing array
if (++i == path.length()) {
break;
}
partMark = i + 1;
arrayMark = -1;
}
}
// add the last element, this should never be an array because they close themselves
if (partMark < path.length()) {
if (quoteMark != -1) {
badFormatJsonPath(path, "unterminated single-quote (')");
}
if (arrayMark != -1) {
badFormatJsonPath(path, "unterminated '['");
}
parts.add(new NestedPathField(path.substring(partMark)));
}
return parts;
}