[JAVA] Comment autoriser les annotations à définir des membres que vous ne souhaitez pas afficher lors de la sérialisation récursive d'une chaîne d'un objet

Vous souhaiterez peut-être combiner le contenu d'un objet dans une chaîne de caractères et l'afficher dans un journal. À ce moment-là, je ne veux pas enregistrer ce champ! Y a-t-il quelque chose comme ça? C'est un peu ennuyeux de se donner la peine d'implémenter toString (), n'est-ce pas?

Chose que tu veux faire

Contenu de l'objet

Cart.java


package jp.co.pmtech.iwata;

public class Cart {
    /**ID du panier*/
    private int id;
    /**Articles dans le panier*/
    private List<Syohin> syohinList;

    //getter ou settert
}

Syohin.java


package jp.co.pmtech.iwata;

public class Syohin {
    /**ID produit*/
    private int id;
    /**Nom du produit*/
    private String name;
    /**prix*/
    private BigDecimal price;

    //getter ou settert
}

App.java


public final class App {
    public static void main(String[] args) {
        Syohin syohin1 = new Syohin();
        syohin1.setId(1);
        syohin1.setName("Stylo à bille");
        syohin1.setPrice(new BigDecimal(120));

        Syohin syohin2 = new Syohin();
        syohin2.setId(2);
        syohin2.setName("tissu");
        syohin2.setPrice(new BigDecimal(298));

        List<Syohin> list = new ArrayList<>();
        list.add(syohin1);
        list.add(syohin2);

        Cart cart = new Cart();
        cart.setId(1);
        cart.setSyohinList(list);
    }
}

Essayez de mettre en œuvre

Ceci peut être réalisé en mettant en œuvre comme suit.

  1. Créez une classe d'annotation pour les champs qui ne sont pas stringifiés.
  2. Annotez le champ cible.
  3. Développez ReflectionToStringBuilder pour ignorer les champs annotés.
  4. Étendez ToStringStyle pour stringify récursivement.

À propos, RecursiveToStringStyle a augmenté de commons-lang3.2, et vous pouvez l'utiliser pour le convertir récursivement en chaîne. Cependant, étant donné que ReflectionToStringBuilder est appelé en interne, le champ à générer ne peut pas être contrôlé. Par conséquent, j'ai décidé d'étendre ToStringStyle par moi-même cette fois. (Les détails de RecursiveToStringStyle seront décrits plus loin dans la partie bonus)

Créer une classe d'annotations

LogIgnore.java


@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogIgnore {
}

Annotation

Cette fois, j'essaierai de ne pas donner le prix du produit (Syohin # price).

Syohin.java


public class Syohin {
    /**ID produit*/
    private int id;
    /**Nom du produit*/
    private String name;
    /**prix*/
    @LogIgnore
    private BigDecimal price;
}

Extension ReflectionToStringBuilder

Il semble que ReflectionToStringBuilder # accept () détermine le champ à stringifier, alors remplacez-le.

LoggingToStringBuilder.java


public class LoggingToStringBuilder extends ReflectionToStringBuilder {

    public LoggingToStringBuilder(final Object object, final ToStringStyle style) {
        super(object, style);
    }

    public static String reflectionToString(final Object object, final ToStringStyle style) {
        LoggingToStringBuilder builder = new LoggingToStringBuilder(object, style) {
            @Override
            protected boolean accept(final Field field) {
                if (!super.accept(field)) {
                    return false;
                }
                if (field.getAnnotation(LogIgnore.class) != null) {
                    return false;
                }
                return true;
            }
        };

        return builder.toString();
    }
}

Extension de ToStringStyle

Avant commons-lang3.2, il semble que vous l'ayez fait vous-même.

Créez une classe qui étend ToStringStyle en vous référant à l'article suivant. (Puisque le nom se trouve être commons-lang, je l'ai créé avec le nom de classe LoggingToStringStyle cette fois.) Sortie rétroactive avec ReflectionToString --A Memorandum

Vous pouvez copier et coller la classe, mais veuillez ne modifier que les points suivants.

LoggingToStringStyle.java


    protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
        for(String packaz : recursivePackags) {
            if (value.getClass().getCanonicalName().startsWith(packaz)) {
                // buffer.append(ToStringBuilder.reflectionToString(value, this));
                //↓ Changer ici
                buffer.append(LoggingToStringBuilder.reflectionToString(value, this));
                return;
            }
        }
        buffer.append(value);
    }

Essayez de bouger

App.config


    String str = LoggingToStringBuilder.toString(cart, new LoggingToStringStyle("jp.co.pmtech.iwata"));
    System.out.println(str);

résultat


jp.co.pmtech.iwata.Cart[id=1,syohinList=[jp.co.pmtech.iwata.Syohin[id=1,name=Stylo à bille],jp.co.pmtech.iwata.Syohin[id=2,name=tissu]]]

Le contenu de Cart et Syohin est correct et le prix n'est pas affiché.

Bonus: d'autres moyens

Conversion JSON

Le but de cette fois est de sortir dans le journal, donc je ne me soucie pas du format de sortie. Affiche la version JSON en utilisant Jackson. Si vous ajoutez l'annotation JsonIgnore, elle ne sera pas soumise à la sérialisation JSON.

Syohin.java


public class Syohin {
    /**ID produit*/
    private int id;
    /**Nom du produit*/
    private String name;
    /**prix*/
    @JsonIgnore
    private BigDecimal price;
}

App.java


    ObjectMapper mapper = new ObjectMapper();
    String str = mapper.writeValueAsString(cart);
    System.out.println(str);

résultat


{"id":1,"syohinList":[{"id":1,"name":"Stylo à bille"},{"id":2,"name":"tissu"}]}

Je ne peux pas obtenir la valeur du prix.

Il n'y a qu'un seul problème, il semble que le contrôleur de repos de Spring Boot soit converti en JSON par Jackson. Je n'ai pas utilisé cette méthode parce que je voulais inclure le prix dans le retour de l'API. Si vous n'utilisez pas Jackson, vous pouvez utiliser cette méthode.

Sortie récursive avec RecursiveToStringStyle de commons-lang

Comme mentionné ci-dessus, RecursiveToStringStyle a été ajouté depuis la version 3.2 de commons-lang. Faisons-le un instant. Mettez le code suivant à la fin de main et exécutez-le.

App.java


    String str = ReflectionToStringBuilder.toString(cart, new RecursiveToStringStyle());
    System.out.println(str);

résultat


jp.co.pmtech.iwata.Cart@610455d6[id=1,syohinList=java.util.ArrayList@2c7b84de{jp.co.pmtech.iwata.Syohin@5a07e868[id=1,name=Stylo à bille,price=java.math.BigDecimal@36baf30c[intVal=<null>,scale=0]],jp.co.pmtech.iwata.Syohin@76ed5528[id=2,name=tissu,price=java.math.BigDecimal@24d46ca6[intVal=<null>,scale=0]]}] 

Il semble qu'il soit converti de manière récursive en chaîne de caractères, mais l'ID de l'objet est affiché et c'est un peu bruyant. De plus, le prix de Big Decimal n'est pas sorti ...

en conclusion

C'est une bonne utilisation, mais c'est peut-être juste une bûche crachée ... Pour des raisons de sécurité, vous ne voulez pas cracher d'informations personnelles et de mots de passe dans le journal. En outre, est-ce un très gros objet que vous souhaitez enregistrer? ??

Selon les exigences, JSON ou Commons-lang RecursiveToStringStyle convient. Le code source est publié sur github, veuillez donc vous y référer. https://github.com/pmt-iwata/LoggingToStringBuilder

Recommended Posts

Comment autoriser les annotations à définir des membres que vous ne souhaitez pas afficher lors de la sérialisation récursive d'une chaîne d'un objet
Comment écrire lorsque vous souhaitez conserver les sauts de ligne et la sortie tout en évitant XSS dans Rails
Comment afficher la valeur lorsqu'il y a un tableau dans le tableau
Comment sortir une chaîne Java sur l'écran de la console
Lorsque vous souhaitez notifier une erreur quelque part lors de l'utilisation de graphql-spring-boot avec Spring Boot