java - example - Aplicar campo no nulo en objeto JSON
map json to java object spring (4)
Nuestra API REST recibe una entrada de algunos objetos JSON donde se requiere que algunos campos no sean nulos. Pueden ser String / Integer o incluso pueden ser otras instancias de clase como referencia.
Estamos tratando de encontrar una manera de hacer que esos campos no sean nulos, en lugar de la forma correcta de verificación nula en la api. Corriente:
if (myObject.getSomeOtherObject() == null) throw new SomeException();
Lo que queremos tener es algo como:
class MyObject{ @Required OtherObject someOtherObject; ... }
Hemos intentado 3 cosas:
Actualice a jackson 2.0.6 y use la anotación com.fasterxml.jackson.annotation.JsonProperty. Pero esto parece que no funciona. Encontré esas referencias: http://jira.codehaus.org/browse/JACKSON-767
Extender JsonDeserializer para verificar nulo pero el problema es que ni siquiera se ejecuta en la entrada nula.
public class NotNullDeserializer<T> extends JsonDeserializer<T> { @Override public T deserialize(JsonParser jsonparser, DeserializationContext deserializationcontext) throws IOException, JsonProcessingException { ParameterizedType superClass = (ParameterizedType) getClass().getGenericSuperclass(); Class type = (Class) 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;
- Escribimos nuestra propia anotación @Required e intentamos verificar con ResourceFilter, pero parece que no puedo obtener el objeto real de ContainerRequest e incluso si pudiéramos, no estamos seguros de cómo realizar una comprobación profunda de la referencia nula en object.otherObject.someObject.fieldNotNullable
private class Filter implements ResourceFilter, ContainerRequestFilter { private final ArrayList requiredParameters; protected Filter() { requiredParameters = null; } protected Filter(ArrayList 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 params = request.getQueryParameters(); params.putAll(request.getFormParameters()); StringBuffer missingParams = new StringBuffer(); for (String reqParam : requiredParameters) { List 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; } }
Cualquier ayuda y conocimientos apreciados.
Puede imponer la validación not null
utilizando una combinación de la biblioteca JSON de Jackson y la javax.validation
junto con el paquete de validación Hibernate.
Si está utilizando Maven, estas son las dependencias que puede usar:
<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>
Luego, su código tendrá que convertir algo de JSON en su objeto anotado y tendrá que validar el objeto utilizando javax.validation.Validator
. Aquí hay un ejemplo de código que muestra cómo se puede hacer esto (vea el método de validate
relevante):
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());
}
}
Aquí hay una clase de muestra usando las anotaciones de validación:
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;
Puede usar JSON-SCHEMA ya que puede expresar muchas restricciones en los campos JSON con él: http://json-schema.org/
Luego, puede generar a partir del esquema sus clases de java con las anotaciones de @NotNull JSR 303 y usar la validación de beans en su objeto. Funciona con Jackson de forma nativa, por lo que no debería tener ningún problema.
Por ejemplo, puede usar el complemento de Maven para hacerlo: http://wiki.jsonschema2pojo.googlecode.com/git/site/0.3.7/generate-mojo.html
@Required
es una anotación de Spring Framework para los granos inyectados, por lo que diría que no la use para este propósito.
Puedes usar este en su lugar:
http://robaustin.wikidot.com/annotations-and-notnull
@NotNull String myString;
Para verificaciones de tiempo de ejecución, intente http://code.google.com/p/notnullcheckweaver/
JAX-RS separa bastante bien la deserialización de la validación, es decir, Jackson no tiene ningún mecanismo para imponer que los valores non-null
, etc. En su lugar, puede usar BeanValidation para eso:
- Agregue una dependencia a
javax.validation:validation-api
en el alcanceprovided
. - Agregue la anotación
javax.validation.constraints.NotNull
a su campo.
Para más detalles, vaya here .