Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,15 @@
<version>1.7R5</version>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.0.0</version>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.1.0</version>
<exclusions>
<exclusion>
<groupId>net.minidev</groupId>
<artifactId>json-smart</artifactId>
</exclusion>
</exclusions>
</dependency>

<!-- Tests -->
Expand Down
48 changes: 31 additions & 17 deletions src/main/java/com/metamx/common/parsers/JSONPathParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,15 @@
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.spi.json.JacksonJsonProvider;
import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider;
import com.metamx.common.Pair;
import com.metamx.common.StringUtils;

import java.math.BigInteger;
import java.nio.charset.CharsetEncoder;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -50,18 +53,24 @@ public class JSONPathParser implements Parser<String, Object>
/**
* Constructor
*
* @param fieldSpecs List of field specifications.
* @param useFieldDiscovery If true, automatically add root fields seen in the JSON document to the parsed object Map.
* Only fields that contain a singular value or flat list (list containing no subobjects or lists) are automatically added.
* @param mapper Optionally provide an ObjectMapper, used by the parser for reading the input JSON.
* @param fieldSpecs List of field specifications.
* @param useFieldDiscovery If true, automatically add root fields seen in the JSON document to the parsed object Map.
* Only fields that contain a singular value or flat list (list containing no subobjects or lists) are automatically added.
* @param mapper Optionally provide an ObjectMapper, used by the parser for reading the input JSON.
*/
public JSONPathParser(List<FieldSpec> fieldSpecs, boolean useFieldDiscovery, ObjectMapper mapper)
{
this.fieldSpecs = fieldSpecs;
this.fieldPathMap = generateFieldPaths(fieldSpecs);
this.useFieldDiscovery = useFieldDiscovery;
this.mapper = mapper == null ? new ObjectMapper() : mapper;
this.jsonPathConfig = Configuration.defaultConfiguration().addOptions(Option.SUPPRESS_EXCEPTIONS);

// Avoid using defaultConfiguration, as this depends on json-smart which we are excluding.
this.jsonPathConfig = Configuration.builder()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add comment as to why defaultConfiguration pulls in json-smart and why that is bad

.jsonProvider(new JacksonJsonProvider())
.mappingProvider(new JacksonMappingProvider())
.options(EnumSet.of(Option.SUPPRESS_EXCEPTIONS))
.build();
}

@Override
Expand All @@ -76,18 +85,23 @@ public void setFieldNames(Iterable<String> fieldNames)
}

/**
* @param input JSON string. The root must be a JSON object, not an array.
* e.g., {"valid": "true"} and {"valid":[1,2,3]} are supported
* but [{"invalid": "true"}] and [1,2,3] are not.
*
* @param input JSON string. The root must be a JSON object, not an array.
* e.g., {"valid": "true"} and {"valid":[1,2,3]} are supported
* but [{"invalid": "true"}] and [1,2,3] are not.
* @return A map of field names and values
* @return A map of field names and values
*/
@Override
public Map<String, Object> parse(String input)
{
try {
Map<String, Object> map = new LinkedHashMap<>();
Map<String, Object> document = mapper.readValue(input, new TypeReference<Map<String, Object>>() {});
Map<String, Object> document = mapper.readValue(
input,
new TypeReference<Map<String, Object>>()
{
}
);
for (Map.Entry<String, Pair<FieldType, JsonPath>> entry : fieldPathMap.entrySet()) {
String fieldName = entry.getKey();
Pair<FieldType, JsonPath> pair = entry.getValue();
Expand Down Expand Up @@ -119,7 +133,7 @@ private Map<String, Pair<FieldType, JsonPath>> generateFieldPaths(List<FieldSpec
Map<String, Pair<FieldType, JsonPath>> map = new LinkedHashMap<>();
for (FieldSpec fieldSpec : fieldSpecs) {
String fieldName = fieldSpec.getName();
if(map.get(fieldName) != null) {
if (map.get(fieldName) != null) {
throw new IllegalArgumentException("Cannot have duplicate field definition: " + fieldName);
}
JsonPath path = JsonPath.compile(fieldSpec.getExpr());
Expand Down Expand Up @@ -167,7 +181,7 @@ private Object valueConversionFunction(Object val)

if (val instanceof List) {
List<Object> newList = new ArrayList<>();
for(Object entry : ((List) val)) {
for (Object entry : ((List) val)) {
newList.add(valueConversionFunction(entry));
}
return newList;
Expand All @@ -176,7 +190,7 @@ private Object valueConversionFunction(Object val)
if (val instanceof Map) {
Map<String, Object> newMap = new LinkedHashMap<>();
Map<String, Object> valMap = (Map<String, Object>) val;
for(Map.Entry<String, Object> entry : valMap.entrySet()) {
for (Map.Entry<String, Object> entry : valMap.entrySet()) {
newMap.put(entry.getKey(), valueConversionFunction(entry.getValue()));
}
return newMap;
Expand Down Expand Up @@ -237,10 +251,10 @@ public static class FieldSpec
/**
* Constructor
*
* @param type Specifies how this field should be retrieved.
* @param name Name of the field, used as the key in the Object map returned by the parser.
* For ROOT fields, this must match the field name as it appears in the JSON document.
* @param expr Only used by PATH type fields, specifies the JsonPath expression used to access the field.
* @param type Specifies how this field should be retrieved.
* @param name Name of the field, used as the key in the Object map returned by the parser.
* For ROOT fields, this must match the field name as it appears in the JSON document.
* @param expr Only used by PATH type fields, specifies the JsonPath expression used to access the field.
*/
public FieldSpec(
FieldType type,
Expand Down