Enforce not-null field в объекте JSON - программирование
Подтвердить что ты не робот

Enforce not-null field в объекте JSON

Наш REST API получает входные данные некоторых объектов JSON, где некоторые поля должны быть не нулевыми. Это может быть либо String/Integer, либо даже другой экземпляр класса в качестве ссылки.

Мы пытаемся найти способ заставить эти поля быть не нулевыми вместо правильного способа проверки на ноль в API. Текущий:

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 для проверки на ноль, но проблема в том, что он даже не выполняется на нулевом вводе.

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

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;
    }
}
4b9b3361

Ответ 1

JAX-RS отлично отделяет десериализацию от проверки, т.е. у Jackson нет механизма для обеспечения значений non-null и т.д. Вместо этого вы можете использовать BeanValidation для этого:

  • Добавить зависимость от javax.validation:validation-api в области provided.
  • Добавьте аннотацию javax.validation.constraints.NotNull в ваше поле.

Подробнее см. здесь.

Ответ 2

Вы можете использовать JSON-SCHEMA, поскольку вы можете выразить много ограничений на поля JSON с ним: http://json-schema.org/

Затем вы можете сгенерировать из схемы ваши классы java с аннотациями @NotNull JSR 303 и использовать валидацию bean на вашем объекте. Он работает с Джексоном изначально, поэтому у вас не должно быть никаких проблем.

Например, вы можете использовать плагин maven для этого: http://wiki.jsonschema2pojo.googlecode.com/git/site/0.3.7/generate-mojo.html

Ответ 3

@Required представляет собой аннотацию фреймворка Spring для инъецированного beans, поэтому я бы сказал, не используйте его для этой цели.

Вместо этого вы можете использовать это:

http://robaustin.wikidot.com/annotations-and-notnull

@NotNull String myString;

Для проверок времени выполнения попробуйте http://code.google.com/p/notnullcheckweaver/

Ответ 4

Вы можете принудительно выполнить not null проверку, используя комбинацию библиотеки Jackson JSON и javax.validation вместе с пакетом валидатора Hibernate.

Если вы используете 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;