Skip to content

Commit db8f5a1

Browse files
committed
Add recordPath option to JsonDecoder (#382)
Instead of using the entire JSON as a single record, provide a JSON path to query the JSON for the records to process, e.g. `$.data` to process every entry in a `data` array as a record.
1 parent 6f3a197 commit db8f5a1

File tree

3 files changed

+63
-13
lines changed

3 files changed

+63
-13
lines changed

metafacture-json/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ description = 'Modules for processing JSON data in Metafacture'
2020
dependencies {
2121
api project(':metafacture-framework')
2222
implementation 'com.fasterxml.jackson.core:jackson-core:2.8.5'
23+
implementation 'com.fasterxml.jackson.core:jackson-databind:2.8.5'
24+
implementation 'com.jayway.jsonpath:json-path:2.6.0'
2325
testImplementation 'junit:junit:4.12'
2426
testImplementation 'org.mockito:mockito-core:2.5.5'
2527
}

metafacture-json/src/main/java/org/metafacture/json/JsonDecoder.java

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,19 @@
1717

1818
import com.fasterxml.jackson.core.JsonFactory;
1919
import com.fasterxml.jackson.core.JsonParser;
20+
import com.fasterxml.jackson.core.JsonProcessingException;
2021
import com.fasterxml.jackson.core.JsonToken;
22+
import com.fasterxml.jackson.databind.ObjectMapper;
23+
import com.jayway.jsonpath.JsonPath;
24+
2125
import org.metafacture.framework.MetafactureException;
2226
import org.metafacture.framework.StreamReceiver;
2327
import org.metafacture.framework.helpers.DefaultObjectPipe;
2428

2529
import java.io.IOException;
30+
import java.util.Arrays;
31+
import java.util.List;
32+
import java.util.stream.Collectors;
2633

2734
/**
2835
* Decodes a record in JSON format.
@@ -38,6 +45,8 @@ public final class JsonDecoder extends DefaultObjectPipe<String, StreamReceiver>
3845

3946
public static final String DEFAULT_RECORD_ID = "%d";
4047

48+
public static final String DEFAULT_ROOT_PATH = "";
49+
4150
private final JsonFactory jsonFactory = new JsonFactory();
4251

4352
private JsonParser jsonParser;
@@ -46,12 +55,15 @@ public final class JsonDecoder extends DefaultObjectPipe<String, StreamReceiver>
4655
private String recordId;
4756
private int recordCount;
4857

58+
private String recordPath;
59+
4960
public JsonDecoder() {
5061
super();
5162

5263
setArrayMarker(DEFAULT_ARRAY_MARKER);
5364
setArrayName(DEFAULT_ARRAY_NAME);
5465
setRecordId(DEFAULT_RECORD_ID);
66+
setRecordPath(DEFAULT_ROOT_PATH);
5567

5668
resetRecordCount();
5769
}
@@ -96,25 +108,45 @@ public int getRecordCount() {
96108
return recordCount;
97109
}
98110

111+
public void setRecordPath(final String recordPath) {
112+
this.recordPath = recordPath;
113+
}
114+
115+
public String getRecordPath() {
116+
return recordPath;
117+
}
118+
99119
public void resetRecordCount() {
100120
setRecordCount(0);
101121
}
102122

103123
@Override
104-
public void process(final String string) {
124+
public void process(final String json) {
105125
assert !isClosed();
106-
107-
createParser(string);
108-
109-
try {
110-
decode();
111-
}
112-
catch (final IOException e) {
113-
throw new MetafactureException(e);
114-
}
115-
finally {
116-
closeParser();
117-
}
126+
final List<String> records = recordPath.isEmpty() ? Arrays.asList(json)
127+
: matches(JsonPath.read(json, recordPath));
128+
records.forEach(record -> {
129+
createParser(record);
130+
try {
131+
decode();
132+
} catch (final IOException e) {
133+
throw new MetafactureException(e);
134+
} finally {
135+
closeParser();
136+
}
137+
});
138+
}
139+
140+
private List<String> matches(Object obj) {
141+
final List<?> records = (obj instanceof List<?>) ? ((List<?>) obj) : Arrays.asList(obj);
142+
return records.stream().map(doc -> {
143+
try {
144+
return new ObjectMapper().writeValueAsString(doc);
145+
} catch (JsonProcessingException e) {
146+
e.printStackTrace();
147+
return doc.toString();
148+
}
149+
}).collect(Collectors.toList());
118150
}
119151

120152
@Override

metafacture-json/src/test/java/org/metafacture/json/JsonDecoderTest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,22 @@ public void testShouldProcessConcatenatedRecords() {
151151
ordered.verify(receiver).endRecord();
152152
}
153153

154+
@Test
155+
public void testShouldProcessRecordsInArray() {
156+
jsonDecoder.setRecordPath("$.data");
157+
jsonDecoder.process(
158+
"{\"data\":[" + "{\"lit\": \"record 1\"}," +
159+
"{\"lit\": \"record 2\"}" + "]}");
160+
161+
final InOrder ordered = inOrder(receiver);
162+
ordered.verify(receiver).startRecord("1");
163+
ordered.verify(receiver).literal("lit", "record 1");
164+
ordered.verify(receiver).endRecord();
165+
ordered.verify(receiver).startRecord("2");
166+
ordered.verify(receiver).literal("lit", "record 2");
167+
ordered.verify(receiver).endRecord();
168+
}
169+
154170
@Test
155171
public void testShouldProcessMultipleRecords() {
156172
jsonDecoder.process("{\"lit\": \"record 1\"}");

0 commit comments

Comments
 (0)