Skip to content

Commit e7c6c2c

Browse files
committed
Correct resolution of location for similar findings
1 parent bdfaee2 commit e7c6c2c

File tree

4 files changed

+58
-308
lines changed

4 files changed

+58
-308
lines changed

jsonvalidator-common/src/main/java/eu/europa/ec/itb/json/validation/JSONValidator.java

Lines changed: 58 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@
1010
import com.gitb.vs.ValidationResponse;
1111
import com.networknt.schema.*;
1212
import com.networknt.schema.i18n.ResourceBundleMessageSource;
13+
import com.networknt.schema.serialization.JsonNodeReader;
14+
import com.networknt.schema.utils.JsonNodes;
1315
import eu.europa.ec.itb.json.DomainConfig;
14-
import eu.europa.ec.itb.json.validation.location.NodeCoordinateDetector;
1516
import eu.europa.ec.itb.validation.commons.*;
1617
import eu.europa.ec.itb.validation.commons.artifact.ValidationArtifactCombinationApproach;
1718
import eu.europa.ec.itb.validation.commons.config.DomainPluginConfigProvider;
@@ -20,6 +21,7 @@
2021
import eu.europa.ec.itb.validation.plugin.ValidationPlugin;
2122
import org.apache.commons.io.FileUtils;
2223
import org.apache.commons.lang3.StringUtils;
24+
import org.apache.commons.lang3.tuple.Pair;
2325
import org.slf4j.Logger;
2426
import org.slf4j.LoggerFactory;
2527
import org.springframework.beans.factory.annotation.Autowired;
@@ -30,8 +32,12 @@
3032
import java.io.IOException;
3133
import java.math.BigInteger;
3234
import java.nio.charset.StandardCharsets;
35+
import java.nio.file.Files;
3336
import java.nio.file.Path;
34-
import java.util.*;
37+
import java.util.ArrayList;
38+
import java.util.LinkedList;
39+
import java.util.List;
40+
import java.util.UUID;
3541
import java.util.function.Function;
3642
import java.util.stream.Collectors;
3743

@@ -131,7 +137,7 @@ public ReportPair validate() {
131137
}
132138
overallReportDetailed.getContext().getItem().add(inputReportContent);
133139
}
134-
if (specs.getDomainConfig().isReportItemCount() && getContentNode() instanceof ArrayNode arrayNode) {
140+
if (specs.getDomainConfig().isReportItemCount() && getContentNode(null) instanceof ArrayNode arrayNode) {
135141
ensureContextCreated(overallReportDetailed);
136142
var countItem = new AnyContent();
137143
countItem.setType("number");
@@ -288,9 +294,9 @@ private void addBranchErrors(List<Message> aggregatedMessages, List<Message> bra
288294
* from local files (and reuse schemas).
289295
*
290296
* @param path The schema path.
291-
* @return The parsed schema.
297+
* @return The parsed schema and JSON node reader to use.
292298
*/
293-
private JsonSchema readSchema(Path path) {
299+
private Pair<JsonSchema, JsonNodeReader> readSchema(Path path) {
294300
try {
295301
var jsonNode = objectMapper.readTree(path.toFile());
296302
var jsonSchemaVersion = JsonSchemaFactory.checkVersion(SpecVersionDetector.detect(jsonNode));
@@ -300,43 +306,77 @@ private JsonSchema readSchema(Path path) {
300306
* may be remotely loaded or schemas that are user-provided. In addition, it allows us to treat schemas that
301307
* may use different specification versions.
302308
*/
309+
var jsonReader = getJsonReader();
303310
var schemaFactory = JsonSchemaFactory.builder()
304311
.schemaLoaders(schemaLoaders -> schemaLoaders.add(new LocalSchemaResolver(specs.getDomainConfig(), localSchemaCache)))
305312
.metaSchema(metaSchema)
306313
.defaultMetaSchemaIri(metaSchema.getIri())
314+
.jsonNodeReader(jsonReader)
307315
.build();
308-
SchemaValidatorsConfig config = new SchemaValidatorsConfig();
309-
config.setPathType(PathType.JSON_POINTER);
310-
config.setLocale(specs.getLocalisationHelper().getLocale());
311-
config.setMessageSource(new ResourceBundleMessageSource("i18n/jsv-messages"));
312-
config.setLocale(this.specs.getLocalisationHelper().getLocale());
313-
return schemaFactory.getSchema(jsonNode, config);
316+
var schemaConfig = SchemaValidatorsConfig.builder()
317+
.pathType(PathType.JSON_POINTER)
318+
.locale(specs.getLocalisationHelper().getLocale())
319+
.messageSource(new ResourceBundleMessageSource("i18n/jsv-messages"))
320+
.build();
321+
return Pair.of(schemaFactory.getSchema(jsonNode, schemaConfig), jsonReader);
314322
} catch (IOException e) {
315323
throw new ValidatorException("validator.label.exception.failedToParseJSONSchema", e, e.getMessage());
316324
}
317325
}
318326

327+
/**
328+
* Create the reader implementation to read the JSON with (is location-aware depending on the configuration).
329+
*
330+
* @return The reader.
331+
*/
332+
private JsonNodeReader getJsonReader() {
333+
var jsonReaderBuilder = JsonNodeReader.builder();
334+
if (!specs.isLocationAsPointer()) {
335+
jsonReaderBuilder = jsonReaderBuilder.locationAware();
336+
}
337+
return jsonReaderBuilder.build();
338+
}
339+
340+
private Function<ValidationMessage, String> getLocationMapper() {
341+
if (specs.isLocationAsPointer()) {
342+
return (msg) -> msg.getInstanceLocation().toString();
343+
} else {
344+
return (msg) -> {
345+
var nodeLocation = JsonNodes.tokenLocationOf(msg.getInstanceNode());
346+
int lineNumber = 0;
347+
if (nodeLocation != null && nodeLocation.getLineNr() > 0) {
348+
lineNumber = nodeLocation.getLineNr();
349+
}
350+
return "%s:%s:0".formatted(ValidationConstants.INPUT_CONTENT, lineNumber);
351+
};
352+
}
353+
}
354+
319355
/**
320356
* Validate the JSON content against one JSON schema.
321357
*
322358
* @param schemaFile The schema file to use.
323359
* @return The resulting error messages.
324360
*/
325361
private List<Message> validateAgainstSchema(File schemaFile) {
326-
var schema = readSchema(schemaFile.toPath());
327-
var content = getContentNode();
328-
return schema.validate(content).stream().map((message) -> new Message(StringUtils.removeStart(message.getMessage(), "[] "), message.getInstanceLocation().toString())).collect(Collectors.toList());
362+
var schemaInfo = readSchema(schemaFile.toPath());
363+
var locationMapper = getLocationMapper();
364+
return schemaInfo.getLeft().validate(getContentNode(schemaInfo.getRight())).stream().map((message) -> new Message(StringUtils.removeStart(message.getMessage(), "[] "), locationMapper.apply(message))).collect(Collectors.toList());
329365
}
330366

331367
/**
332368
* Parse the JSON node for the provided input file.
333369
*
334370
* @return The JSON node.
335371
*/
336-
private JsonNode getContentNode() {
372+
private JsonNode getContentNode(JsonNodeReader reader) {
337373
if (contentNode == null) {
338-
try {
339-
contentNode = objectMapper.readTree(specs.getInputFileToUse());
374+
try (var input = Files.newInputStream(specs.getInputFileToUse().toPath())) {
375+
if (reader != null) {
376+
contentNode = reader.readTree(input, InputFormat.JSON);
377+
} else {
378+
contentNode = objectMapper.readTree(input);
379+
}
340380
} catch (IOException e) {
341381
throw new ValidatorException("validator.label.exception.failedToParseJSON", e);
342382
}
@@ -386,17 +426,10 @@ private ReportPair createReport(List<Message> messages) {
386426
} else {
387427
report.setResult(TestResultType.FAILURE);
388428
report.getCounters().setNrOfErrors(BigInteger.valueOf(messages.size()));
389-
390-
Function<String, String> locationSupplier;
391-
if (specs.isLocationAsPointer()) {
392-
locationSupplier = (contentPath) -> contentPath;
393-
} else {
394-
locationSupplier = new NodeCoordinateDetector(specs.getInputFileToUse());
395-
}
396429
for (var message: messages) {
397430
BAR error = new BAR();
398431
error.setDescription(message.getDescription());
399-
error.setLocation(locationSupplier.apply(message.getContentPath()));
432+
error.setLocation(message.getContentPath());
400433
var elementForReport = objectFactory.createTestAssertionGroupReportsTypeError(error);
401434
report.getReports().getInfoOrWarningOrError().add(elementForReport);
402435
if (aggregateReportItems != null) {

jsonvalidator-common/src/main/java/eu/europa/ec/itb/json/validation/location/CustomJsonNodeFactory.java

Lines changed: 0 additions & 183 deletions
This file was deleted.

jsonvalidator-common/src/main/java/eu/europa/ec/itb/json/validation/location/CustomParserFactory.java

Lines changed: 0 additions & 27 deletions
This file was deleted.

0 commit comments

Comments
 (0)