REST APIは、一部のフィールドがnullでないことが必要なJSONオブジェクト入力を受け取ります。これらは、参照としてString/Integerまたは他のクラスインスタンスである場合があります。
APIでのnullチェックの正しい方法の代わりに、これらのフィールドをnull以外に強制する方法を見つけようとしています。電流:
if (myObject.getSomeOtherObject() == null)
throw new SomeException();
私たちが持ちたいのは次のようなものです:
class MyObject{
@Required
OtherObject someOtherObject;
// ...
}
3つのことを試しました。
1)jackson 2.0.6にアップグレードし、注釈com.fasterxml.jackson.annotation.JsonPropertyを使用しますが、これは機能していないように見えます。それらの参照を見つけました: http://jira.codehaus.org/browse/JACKSON-767
2)JsonDeserializerを拡張してnullをチェックしますが、問題はnull入力に対しても実行されないことです。
public class NotNullDeserializer<T> extends JsonDeserializer<T> {
@Override
public T deserialize(JsonParser jsonparser, DeserializationContext deserializationcontext) throws IOException, JsonProcessingException {
ParameterizedType superClass = (ParameterizedType) getClass().getGenericSuperclass();
Class<T> type = (Class<T>) superClass.getActualTypeArguments()[0];
T t = jsonparser.readValueAs(type);
if (t == null){
String classNameField = type.getName();
String field = jsonparser.getCurrentName();
throw new WrongInputException("The field '"+field+"' of type '"+classNameField+"' should not be null.");
}
return t;
}
}
public class NotNullAddressDeserializer extends NotNullDeserializer<Address> {
}
@JsonDeserialize(using=NotNullAddressDeserializer.class)
Address to;
3)独自の@Required
アノテーションを記述してResourceFilterでチェックしようとしていますが、ContainerRequestから実際のオブジェクトを取得できず、object.otherObject.someObject.fieldNotNullable
でnull参照のディープチェックを実行する方法がわからない
private class Filter implements ResourceFilter, ContainerRequestFilter {
private final ArrayList<String> requiredParameters;
protected Filter() {
requiredParameters = null;
}
protected Filter(ArrayList<String> requiredParameters) {
this.requiredParameters = requiredParameters;
}
@Override
public ContainerRequestFilter getRequestFilter() {
return this;
}
@Override
public ContainerResponseFilter getResponseFilter() {
return null;
}
@Override
public ContainerRequest filter(ContainerRequest request) {
if (requiredParameters != null && requiredParameters.size() > 0) {
MultivaluedMap<String, String> params = request.getQueryParameters();
params.putAll(request.getFormParameters());
StringBuffer missingParams = new StringBuffer();
for (String reqParam : requiredParameters) {
List<String> paramValues = params.get(reqParam);
if (paramValues == null || paramValues != null && paramValues.size() == 0)
missingParams.append(reqParam + ",");
}
if (missingParams.length() > 0)
throw new WrongInputException("Required parameters are missing: " + missingParams);
}
return request;
}
}
JAX-RSは、デシリアライゼーションと検証を非常にうまく分離しています。つまり、ジャクソンには、設計によって値をnon-null
などにするメカニズムがありません。代わりに、BeanValidationを使用できます。
provided
スコープのjavax.validation:validation-api
に依存関係を追加します。javax.validation.constraints.NotNull
注釈をフィールドに追加します。詳細については、 こちら をご覧ください。
JSONフィールドで多くの制約を表現できるため、JSON-SCHEMAを使用できます。 http://json-schema.org/
その後、スキーマからJava @NotNull JSR 303注釈付きのクラスを生成し、オブジェクトでBean検証を使用できます。これはJacksonでネイティブに機能するため、問題はありません。
たとえば、mavenプラグインを使用してそうすることができます。 http://wiki.jsonschema2pojo.googlecode.com/git/site/0.3.7/generate-mojo.html
@Required
はインジェクトされたBeanのSpringフレームワークアノテーションであるため、この目的には使用しないでください。
代わりにこれを使用できます:
http://robaustin.wikidot.com/annotations-and-notnull
@NotNull String myString;
ランタイムチェックについては、 http://code.google.com/p/notnullcheckweaver/ を試してください。
Jackson JSONライブラリとnot null
をHibernateバリデータパッケージと組み合わせて使用すると、javax.validation
検証を実施できます。
Mavenを使用している場合、使用できる依存関係は次のとおりです。
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson-version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson-version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson-version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>${hibernate-validator.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator-annotation-processor</artifactId>
<version>${hibernate-validator.version}</version>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.6</version>
</dependency>
</dependencies>
次に、コードでJSONを注釈付きオブジェクトに変換する必要があり、javax.validation.Validator
を使用してオブジェクトを検証する必要があります。これを行う方法を示すサンプルコードを次に示します(関連するvalidate
メソッドを参照)。
public class ShareLocationService {
private ObjectMapper mapper = new ObjectMapper();
private ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
// Materialize the Java object which contains the validation annotations
public ShareLocation readFrom(String json) throws IOException {
return mapper.readerFor(ShareLocation.class).readValue(json);
}
// validate and collect the set of validations
public Set<String> validate(String json) throws IOException {
ShareLocation shareMyLocation = readFrom(json);
Validator validator = factory.getValidator();
Set<ConstraintViolation<ShareLocation>> violations = validator.validate(shareMyLocation);
return violations.stream().map(Object::toString).collect(Collectors.toSet());
}
}
検証アノテーションを使用したサンプルクラスを次に示します。
public class ShareLocation {
@JsonProperty("Source")
@NotNull
private String source;
@JsonProperty("CompanyCode")
private String companyCode;
@JsonProperty("FirstName")
private String firstName;
@JsonProperty("LastName")
private String lastName;
@JsonProperty("Email")
private String email;
@JsonProperty("MobileNumber")
private String mobileNumber;
@JsonProperty("Latitude")
@NotNull
private Double latitude;
@JsonProperty("Longitude")
@NotNull
private Double longitude;
@JsonProperty("LocationDateTime")
@NotNull
private String locationDateTime;