org.everit.json.schema.ValidationException Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.everit.json.schema Show documentation
Show all versions of org.everit.json.schema Show documentation
Implementation of the JSON Schema Core Draft v4 specification built with the org.json API
The newest version!
/*
* Copyright (C) 2011 Everit Kft. (http://www.everit.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.everit.json.schema;
import org.json.JSONArray;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import static java.util.Objects.requireNonNull;
/**
* Thrown by {@link Schema} subclasses on validation failure.
*/
public class ValidationException extends RuntimeException {
private static final long serialVersionUID = 6192047123024651924L;
private static int getViolationCount(final List causes) {
int causeCount = causes.stream().mapToInt(ValidationException::getViolationCount).sum();
return Math.max(1, causeCount);
}
private static List getAllMessages(final List causes) {
List messages = causes.stream()
.filter(cause -> cause.causingExceptions.isEmpty())
.map(ValidationException::getMessage)
.collect(Collectors.toList());
messages.addAll(causes.stream()
.filter(cause -> !cause.causingExceptions.isEmpty())
.flatMap(cause -> getAllMessages(cause.getCausingExceptions()).stream())
.collect(Collectors.toList()));
return messages;
}
/**
* Sort of static factory method. It is used by {@link ObjectSchema} and {@link ArraySchema} to
* create {@code ValidationException}s, handling the case of multiple violations occuring during
* validation.
*
*
* - If {@code failures} is empty, then it doesn't do anything
* - If {@code failures} contains 1 exception instance, then that will be thrown
* - Otherwise a new exception instance will be created, its {@link #getViolatedSchema()
* violated schema} will be {@code rootFailingSchema}, and its {@link #getCausingExceptions()
* causing exceptions} will be the {@code failures} list
*
*
* @param rootFailingSchema the schema which detected the {@code failures}
* @param failures list containing validation failures to be thrown by this method
*/
public static void throwFor(final Schema rootFailingSchema,
final List failures) {
int failureCount = failures.size();
if (failureCount == 0) {
return;
} else if (failureCount == 1) {
throw failures.get(0);
} else {
throw new ValidationException(rootFailingSchema, new ArrayList<>(failures));
}
}
private final StringBuilder pointerToViolation;
private final transient Schema violatedSchema;
private final List causingExceptions;
private final String keyword;
/**
* Deprecated, use {@code ValidationException(Schema, Class>, Object)} instead.
*
* @param expectedType the expected type
* @param actualValue the violating value
*/
@Deprecated
public ValidationException(final Class> expectedType, final Object actualValue) {
this(null, expectedType, actualValue);
}
/**
* Constructor, creates an instance with {@code keyword="type"}.
*
* @param violatedSchema the schema instance which detected the schema violation
* @param expectedType the expected type
* @param actualValue the violating value
*/
public ValidationException(final Schema violatedSchema, final Class> expectedType,
final Object actualValue) {
this(violatedSchema, expectedType, actualValue, "type");
}
/**
* Constructor for type-mismatch failures. It is usually more convenient to use
* {@link #ValidationException(Schema, Class, Object)} instead.
*
* @param violatedSchema the schema instance which detected the schema violation
* @param expectedType the expected type
* @param actualValue the violating value
* @param keyword the violating keyword
*/
public ValidationException(final Schema violatedSchema, final Class> expectedType,
final Object actualValue, final String keyword) {
this(violatedSchema, new StringBuilder("#"),
"expected type: " + expectedType.getSimpleName() + ", found: "
+ (actualValue == null ? "null" : actualValue.getClass().getSimpleName()),
Collections.emptyList(), keyword);
}
private ValidationException(final Schema rootFailingSchema,
final List causingExceptions) {
this(rootFailingSchema, new StringBuilder("#"),
getViolationCount(causingExceptions) + " schema violations found",
causingExceptions);
}
/**
* Constructor.
*
* @param violatedSchema the schema instance which detected the schema violation
* @param message the readable exception message
* @deprecated use one of the constructors which explicitly specify the violated keyword instead
*/
@Deprecated
public ValidationException(final Schema violatedSchema, final String message) {
this(violatedSchema, new StringBuilder("#"), message, Collections.emptyList());
}
/**
* Constructor.
*
* @param violatedSchema the schama instance which detected the schema violation
* @param message the readable exception message
* @param keyword the violated keyword
*/
public ValidationException(final Schema violatedSchema,
final String message,
final String keyword) {
this(violatedSchema,
new StringBuilder("#"),
message,
Collections.emptyList(),
keyword);
}
/***
* Constructor.
*
* @param violatedSchema
* the schema instance which detected the schema violation
* @param pointerToViolation
* a JSON pointer denoting the part of the document which violates the schema
* @param message
* the readable exception message
* @param causingExceptions
* a (possibly empty) list of validation failures. It is used if multiple schema
* violations are found by violatedSchema
* @deprecated please explicitly specify the violated keyword using one of these constructors:
*
* - {@link #ValidationException(Schema, StringBuilder, String, List, String)}
*
- {@link #ValidationException(Schema, String, String)}
*
- {@link #ValidationException(Schema, Class, Object, String)}
*
*/
@Deprecated ValidationException(final Schema violatedSchema, final StringBuilder pointerToViolation,
final String message,
final List causingExceptions) {
this(violatedSchema, pointerToViolation, message, causingExceptions, null);
}
/***
* Constructor.
*
* @param violatedSchema
* the schema instance which detected the schema violation
* @param pointerToViolation
* a JSON pointer denoting the part of the document which violates the schema
* @param message
* the readable exception message
* @param causingExceptions
* a (possibly empty) list of validation failures. It is used if multiple schema
* violations are found by violatedSchema
* @param keyword
* the violated keyword
*/
ValidationException(final Schema violatedSchema, final StringBuilder pointerToViolation,
final String message,
final List causingExceptions,
final String keyword) {
super(message);
this.violatedSchema = violatedSchema;
this.pointerToViolation = pointerToViolation;
this.causingExceptions = Collections.unmodifiableList(causingExceptions);
this.keyword = keyword;
}
/**
* Deprecated, use {@code ValidationException(Schema, String)} instead.
*
* @param message readable exception message
*/
@Deprecated
public ValidationException(final String message) {
this((Schema) null, new StringBuilder("#"), message, Collections.emptyList());
}
private ValidationException(final StringBuilder pointerToViolation,
final Schema violatedSchema,
final String message,
final List causingExceptions,
final String keyword) {
this(violatedSchema, pointerToViolation, message, causingExceptions, keyword);
}
/**
* Constructor.
*
* @param violatedSchema the schema instance which detected the schema violation
* @param message the readable exception message
* @param causingExceptions a (possibly empty) list of validation failures. It is used if multiple schema
* violations are found by violatedSchema
* @deprecated use one of the constructors which explicitly specify the keyword instead
*/
@Deprecated
public ValidationException(final Schema violatedSchema, final String message,
final List causingExceptions) {
this(violatedSchema, new StringBuilder("#"), message, causingExceptions);
}
private String escapeFragment(final String fragment) {
return fragment.replace("~", "~0").replace("/", "~1");
}
public List getCausingExceptions() {
return causingExceptions;
}
/**
* Returns all messages collected from all violations, including nested causing exceptions.
* @return all messages
*/
public List getAllMessages() {
if (causingExceptions.isEmpty()) {
return Collections.singletonList(getMessage());
} else {
return getAllMessages(causingExceptions).stream().collect(Collectors.toList());
}
}
/**
* Returns a programmer-readable error description prepended by {@link #getPointerToViolation()
* the pointer to the violating fragment} of the JSON document.
*
* @return the error description
*/
@Override
public String getMessage() {
return getPointerToViolation() + ": " + super.getMessage();
}
/**
* Returns a programmer-readable error description. Unlike {@link #getMessage()} this doesn't
* contain the JSON pointer denoting the violating document fragment.
*
* @return the error description
*/
public String getErrorMessage() {
return super.getMessage();
}
/**
* A JSON pointer denoting the part of the document which violates the schema. It always points
* from the root of the document to the violating data fragment, therefore it always starts with
* #
.
*
* @return the JSON pointer
*/
public String getPointerToViolation() {
if (pointerToViolation == null) {
return null;
}
return pointerToViolation.toString();
}
public Schema getViolatedSchema() {
return violatedSchema;
}
/**
* Creates a new {@code ViolationException} instance based on this one, but with changed
* {@link #getPointerToViolation() JSON pointer}.
*
* @param fragment the fragment of the JSON pointer to be prepended to existing pointers
* @return the new instance
*/
public ValidationException prepend(final String fragment) {
return prepend(fragment, this.violatedSchema);
}
/**
* Creates a new {@code ViolationException} instance based on this one, but with changed
* {@link #getPointerToViolation() JSON pointer} and {link {@link #getViolatedSchema() violated
* schema}.
*
* @param fragment the fragment of the JSON pointer to be prepended to existing pointers
* @param violatedSchema the violated schema, which may not be the same as {@link #getViolatedSchema()}
* @return the new {@code ViolationException} instance
*/
public ValidationException prepend(final String fragment, final Schema violatedSchema) {
String escapedFragment = escapeFragment(requireNonNull(fragment, "fragment cannot be null"));
StringBuilder newPointer = this.pointerToViolation.insert(1, '/').insert(2, escapedFragment);
List prependedCausingExceptions = causingExceptions.stream()
.map(exc -> exc.prepend(escapedFragment))
.collect(Collectors.toList());
return new ValidationException(newPointer, violatedSchema, super.getMessage(),
prependedCausingExceptions, this.keyword);
}
public int getViolationCount() {
return getViolationCount(causingExceptions);
}
public String getKeyword() {
return keyword;
}
/**
* Creates a JSON representation of the failure.
*
* The returned {@code JSONObject} contains the following keys:
*
* - {@code "message"}: a programmer-friendly exception message. This value is a non-nullable
* string.
* - {@code "keyword"}: a JSON Schema keyword which was used in the schema and violated by the
* input JSON. This value is a nullable string.
* - {@code "pointerToViolation"}: a JSON Pointer denoting the path from the root of the
* document to the invalid fragment of it. This value is a non-nullable string. See
* {@link #getPointerToViolation()}
* - {@code "causingExceptions"}: is a (possibly empty) array of violations which caused this
* exceptions. See {@link #getCausingExceptions()}
*
*
* @return a JSON description of the validation error
*/
public JSONObject toJSON() {
JSONObject rval = new JSONObject();
rval.put("keyword", keyword);
if (pointerToViolation == null) {
rval.put("pointerToViolation", JSONObject.NULL);
} else {
rval.put("pointerToViolation", getPointerToViolation());
}
rval.put("message", super.getMessage());
List causeJsons = causingExceptions.stream()
.map(ValidationException::toJSON)
.collect(Collectors.toList());
rval.put("causingExceptions", new JSONArray(causeJsons));
return rval;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy