web-dev-qa-db-fra.com

Classe annotée de Lombok immuable avec Jackson

Quelle est la méthode préférée pour créer une classe qui est

  • Immuable
  • Peut être sérialisé/désérialisé avec Jackson
  • Lisible par l'homme et avec un faible niveau de passe-partout

De préférence, j'aurais aimé que quelque chose comme ça marche:

@Data(onConstructor = @__(@JsonCreator))

et puis tous les champs à être private final. Cependant, cela ne compile même pas (et je ne sais pas pourquoi). En utilisant 

@AllArgsConstructor(onConstructor = @__(@JsonCreator))

compilera mais seulement les rendements 

InvalidDefinitionException: No serializer found for class
4
Christian Neverdal

Une autre alternative qui:

  • répond aux critères 
  • a moins de chaudière que la meilleure réponse actuelle
  • fonctionne sur v1.16.20 (9 janvier 2018) et plus tard

est d'ajouter ConstructorProperties :

  • Créez un fichier lombok.configdans un emplacement approprié avec la ligne suivante: lombok.anyConstructor.addConstructorProperties = true
  • Ajoutez une annotation lombok @Value à votre classe pour la rendre immuable

Ensuite, la sérialisation et la désérialisation par Jackson fonctionnent comme prévu. 

6
robSE13

Vous pouvez utiliser @Builder annotation de Lombok pour générer un générateur pour votre classe POJO immuable. Mais rendre le générateur généré par Lombok utilisable par la désérialisation de Jackson est un peu délicat.

Exemple:

Une classe immuable de POJO:

@Data
@Builder(builderClassName = "PointBuilder")
@JsonDeserialize(builder = Point.PointBuilder.class)
public class Point {

    private final int x;

    private final int y;

    @JsonPOJOBuilder(withPrefix = "")
    public static class PointBuilder {
        // Lombok will add constructor, setters, build method
    }
}

 POJO outline

Voici un test JUnit pour vérifier la sérialisation/désérialisation:

public class PointTest extends Assert {

    private ObjectMapper objectMapper = new ObjectMapper();

    @Test
    public void testSerialize() throws IOException {
        Point point = new Point(10, 20);
        String json = objectMapper.writeValueAsString(point);
        assertEquals("{\"x\":10,\"y\":20}", json);
    }

    @Test
    public void testDeserialize() throws IOException {
        String json = "{\"x\":10,\"y\":20}";
        Point point = objectMapper.readValue(json, Point.class);
        assertEquals(new Point(10, 20), point);
    }
}
9
Thomas Fritsch

Une autre alternative beaucoup moins verbeuse:

@Data
@Setter(AccessLevel.NONE)
public class Clazz {
    private String field;
} 

Bien sûr, vous pouvez toujours avoir une méthode privée qui modifie le champ directement, mais il serait très improbable d'avoir même du code dans un POJO @Data, donc cela ne se produirait pas.

Disclaimer: Ceci aura pour effet secondaire (peut-être bénéfique) de ne pas laisser le code Java normal créer l'objet car il n'y a qu'un constructeur par défaut sans mutateurs. Pour permettre une construction normale, vous aurez besoin de 2 annotations supplémentaires:

@Data
@AllArgsConstructor
@NoArgsConstructor
@Setter(AccessLevel.NONE)
public class Clazz {
    private String field;
} 
1