web-dev-qa-db-fra.com

Java 8 validation Jackson

J'ai un service de repos Springboot. L'utilisateur passe un objet json qui est désérialisé dans ce Java pojo:

public final class Request {
    private String id;
    private double code;
    private String name;

    public String getId() {
        return id;
    }

    public double getCode() {
        return code;
    }

    public String getName() {
        return name;
    }
}

L'utilisateur doit donc passer le json suivant:

{
    "id": “123457896”,
    "code": "Foo",
    "name": "test"
} 

Je veux rendre tous ces champs obligatoires. Fournir quelque chose de moins ou de plus lèvera une exception. Existe-t-il un moyen de dire à jackson de valider l'entrée lorsqu'elle se désérialise? J'ai essayé @JsonProperty(required=true) mais cela ne fonctionne pas; apparemment de ici et ici il semble que l'annotation JsonProperty ne soit pas respectée.

J'ai ce validateur que j'appelle dans mon contrôleur:

@Component
public class RequestValidator implements Validator {
    @Override
    public boolean supports(Class<?> clazz) {
        return false;
    }

    @Override
    public void validate(Object target, Errors errors) {
        String id = ((Request) target).getId();
        if(id == null || id.isEmpty()) {
            throw new InvalidRequestException("A valid id is missing. Please provide a non-empty or non-null id.");
        }
    }
}

Mais cela semble juste fastidieux et laid pour vérifier chaque champ. Donc, étant donné que j'utilise Java 8, Spring Boot et la dernière version de jackson, quelle est la meilleure pratique en termes de validation d'une entrée json entrante? Ou est-ce que je le fais déjà le plus manière date?

11
Richard

Vous avez utilisé l'approche Spring Validator. Il existe une autre approche:

API de validation de bean J2EE JSR-303/JSR-349. il fournit des annotations de validation (javax, pas jackson).

Voir un bon exemple des deux ici

11
Dennis R

Il y a pas besoin de validateur personnalisé. Il y a un moyen de dire à jackson de lancer

  • JsonMappingException si vous n'avez pas de champs obligatoires

  • UnrecognizedPropertyException si vous avez des champs supplémentaires (UnrecognizedPropertyException est simplement étendu JsonMappingException).

Vous avez juste besoin d'ajouter @JsonCreator ou constructeur personnalisé. Quelque chose comme ça devrait fonctionner:

public Request(@JsonProperty(value= "id", required = true)String id,
               @JsonProperty(value= "code",required = true)double code,
               @JsonProperty(value= "name",required = true)String name) {
    this.id = id;
    this.code = code;
    this.name = name;
}

Démo complète:

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import Java.io.IOException;

public class Main {

public static void main(String[] args) throws IOException {
    test("{\"id\": \"123457896\",\"code\": 1,\"name\": \"test\"}");
    test("{\"id\": \"123457896\",\"name\": \"test\"}");
    test("{\"id\": \"123457896\",\"code\": 1, \"c\": 1,\"name\": \"test\"}");
}

public static void test(String json) throws IOException{
    ObjectMapper mapper = new ObjectMapper();
    try {
        Request deserialized = mapper.readValue(json, Request.class);
        System.out.println(deserialized);
        String serialized = mapper.writeValueAsString(deserialized);
        System.out.println(serialized);
    } catch (JsonMappingException e) {
        System.out.println(e.getMessage());
    }
}

public static class Request {
    private String id;
    private double code;
    private String name;

    public Request(@JsonProperty(value= "id", required = true)String id,
                   @JsonProperty(value= "code",required = true)double code,
                   @JsonProperty(value= "name",required = true)String name) {
        this.id = id;
        this.code = code;
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public double getCode() {
        return code;
    }

    public void setCode(double code) {
        this.code = code;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Request{" +
                "id='" + id + '\'' +
                ", code=" + code +
                ", name='" + name + '\'' +
                '}';
    }
}
}

Résultat:

Request{id='123457896', code=1.0, name='test'}
{"id":"123457896","code":1.0,"name":"test"}
Missing required creator property 'code' (index 1)
 at [Source: {"id": "123457896","name": "test"}; line: 1, column: 34]
Unrecognized field "c" (class Main7$Request), not marked as ignorable (3 known properties: "id", "code", "name"])
 at [Source: {"id": "123457896","code": 1, "c": 1,"name": "test"}; line: 1, column: 53] (through reference chain: Request["c"])
14
varren

Vous pouvez utiliser la validation du schéma Jackson. Créez un schéma.json et validez chaque entrée entrante par rapport à ce schéma. Suivez ce lien pour plus de détails http://wilddiary.com/validate-json-against-schema-in-Java/

2
tyagi

En utilisant le @JsonCreator annotation sur le constructeur avec @JsonProperty marchera. Vérifiez également réponse .

public final class Request {
    private String id;
    private double code;
    private String name;

    @JsonCreator
    public Request(@JsonProperty(required = true) String id,
                   @JsonProperty(required = true) double code,
                   @JsonProperty(required = true) String name) {
        this.id = id;
        this.code = code;
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public double getCode() {
        return code;
    }

    public String getName() {
        return name;
    }
}
2
octavian