1
votes

Créer des POJO / beans dynamiquement et définir des valeurs à l'aide de CGLib

J'ai besoin d'analyser un fichier texte et de générer un document JSON. Le fichier texte a un modèle de texte qui contient une clé qui est un nom et la valeur est un énorme texte de TSV avec des en-têtes.

Je pourrais analyser le fichier texte et générer des classes de bean en utilisant les en-têtes et maintenant je veux définir les données sur cette classe de bean générée. J'utilise la réflexion pour faire cela.

Map<String, List<Map<String, String>>> finalMap = new HashMap<>();
        metadataMap.forEach((k, v) -> {

            List<Map<String, String>> datamap = new ArrayList<>();

            String key = k;
            String[] fields = v.getFields();
            List<String> lines = v.getLines();


            lines.forEach(line -> {
                if (line != null && !line.isEmpty() && !line.equals("null")) {
                    String[] fieldData = line.split("\t");
                    Map<String, String> eachLineMap = new HashMap<>();
                    for (int index = 0; index < fields.length; index++) {
                        if (index < fieldData.length && (fieldData[index] != null && !fieldData[index].isEmpty())) {
                            eachLineMap.put(fields[index], fieldData[index]);
                        } else {
                            eachLineMap.put(fields[index], " ");
                        }
                        datamap.add(eachLineMap);
                    }
                }
            });
            finalMap.put(key, datamap);
        });

        try {
            output = new ObjectMapper().writeValueAsString(finalMap);
        }catch(JsonProcessingException e){
            e.printStackTrace();
        }

Le problème avec l'approche est que la plupart du temps, toutes les valeurs de colonne peuvent ne pas avoir de données, elles peuvent être annulées. P >

Je me demande s'il existe un moyen plus simple de faire cela. Toute aide est appréciée.

Voici la méthode de génération de bean.

[{
    "<Data1>": [{
            "col1": "",
            "col2": "",
            "col3": "",
            "col4": ""
        },
        {
            "col1": "",
            "col2": "",
            "col3": "",
            "col4": ""
        },
        {
            "col1": "",
            "col2": "",
            "col3": "",
            "col4": ""
        }
    ]

}, {
    "<Data2>": [{
            "col1": "",
            "col2": "",
            "col3": "",
            "col4": "",
            "col5": "",
            "col6": "",
            "col7": "",
            "col8": ""
        },
        {
            "col1": "",
            "col2": "",
            "col3": "",
            "col4": "",
            "col5": "",
            "col6": "",
            "col7": "",
            "col8": ""
        },
        {
            "col1": "",
            "col2": "",
            "col3": "",
            "col4": "",
            "col5": "",
            "col6": "",
            "col7": "",
            "col8": ""
        }
    ]

}]

Voici l'exemple de fichier texte qui doit être converti en sortie JSON.

<Data1>
Col1  col2 col3 col4 col5
even    sense   met has
root    greatest    spin    mostly
gentle  held    introduced  palace
cold    equator remember    grandmother
slightly    butter  depth   like
distant second  coast   everyone


<Data2>
Col1  col2 col3 col4 col5 col6 col7 col8
greatest    rope    operation   flies   brown   continent   combination read
slightly    diagram he  grandfather where   party   fifty   pour
well    put plastic anyway  refer   careful correct furniture
how since   army    tongue  birthday    been    clock   official
table   command specific    distant cutting hill    movie   experience
national    though  stopped youth   army    underline   five    know

<Data3>
Col1 col2 col3 col4 col5 col6 col7 col8 col9 col9 col10
vessels characteristic  ship    joy than    tomorrow    high    seven   future  trade
try gray    fourth  advice  week    stream  motion  musical whom    tin
limited daughter    large   rice    came    home    chicken wheat   engine  box
easy    city    pair    strange stage   visitor coach   announced   allow   simple
jet therefore   single  during  construction    flag    bigger  muscle  complex pleasure
income  several coat    range   dull    cattle  damage  jump    present shake

Sortie JSON:

public static Class<?> beanGenerator(final String className, final Map<String, Class<?>> properties) {
        BeanGenerator beanGenerator = new BeanGenerator();
        beanGenerator.setNamingPolicy(new NamingPolicy() {
            @Override
            public String getClassName(String prefix, String source, Object key, Predicate names) {
                return className;
            }
        });

        BeanGenerator.addProperties(beanGenerator, properties);
        return (Class<?>) beanGenerator.createClass();
    }

J'ai trouvé une solution en utilisant les cartes.

Class<?> beanClass = BeanClassGenerator.beanGenerator(k, mapForBeanGeneration);
            try {
                Object beanClassObject = beanClass.newInstance();
                lines.forEach(line -> {
                    if (line != null && !line.isEmpty() && !line.equals("null")) {
                        String[] lineData = line.split("\t");
                        System.out.println("LineData length :: " + lineData.length);
                        Method[] methods = beanClass.getMethods();
                        System.out.println("Methods length :: " + methods.length);
                        int index = 0;
                        for (Method m : methods) {
                            m.setAccessible(true);
                            if (m.getName().startsWith("set")) {
                                try {
                                    if ((lineData.length <= index) && lineData[index] != null) {
                                        m.invoke(beanClassObject, lineData[index]);
                                        index++;
                                    } else {
                                        m.invoke(beanClassObject, " ");
                                    }
                                } catch (IllegalAccessException | InvocationTargetException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                });
                ObjectMapper om = new ObjectMapper();
                System.out.println(om.writeValueAsString(beanClassObject));
            } catch (InstantiationException | IllegalAccessException | JsonProcessingException e) {
                e.printStackTrace();
  }});


2 commentaires

Donnez un échantillon du fichier que vous lisez et du JSON attendu.


@DwB Ajout du fichier texte d'entrée et de la sortie qui doit être généré.


3 Réponses :


1
votes

Vous n'avez pas besoin d'écrire toute cette logique, vous pouvez simplement utiliser Apache Commons BeanUtils ; qui fournit une méthode utilitaire (parmi BEAUCOUP autres utilitaires), qui prend une Map des noms de champs par rapport aux valeurs de champ et remplit un bean donné avec:

import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import org.apache.commons.beanutils.BeanUtils;

import lombok.Data;

public class DynamicBeanUtils {

    static Map<String, String> createFieldNameValueMap(String headerLine, String valuesLine) {
        String[] fieldNames = headerLine.split("\t");
        String[] fieldValues = valuesLine.split("\t");

        return IntStream.range(0, fieldNames.length)
            .mapToObj(Integer::new)
            .collect(Collectors.toMap(idx -> fieldNames[idx], idx -> fieldValues[idx]));
    }

    public static void main(String[] args) {
        String headerLine = "booleanValue\tintValue\tstringValue\tdoubleValue\totherValue";
        String valuesLine = "true\t12\tthis bean will be populated\t22.44\ttest string!!!";

        Object target = new MyBean();
        try {
            BeanUtils.populate(target, createFieldNameValueMap(headerLine, valuesLine));
        } catch (IllegalAccessException | InvocationTargetException e) {
            // HANDLE EXCEPTIONS!
        }

        System.out.println(target);
    }

    @Data
    public static class MyBean {
        private String stringValue;
        private double doubleValue;
        private int intValue;
        private boolean booleanValue;
        private String otherValue;
    }
}


2 commentaires

Merci pour la réponse élaborée. Dans mon cas, le fichier texte d'entrée a plusieurs ensembles de données TSV et chaque ensemble de données peut avoir plusieurs colonnes. L'approche ci-dessus ne fonctionne pas pour moi. Veuillez aider.


C'est encore plus simple, utilisez simplement new JSONObject (map) avec map étant les données de chaque enregistrement unique chargé dans une Map des noms de champs et des valeurs et les sérialiser en json à partir de là.



1
votes

Vous allez trop loin avec votre solution.
Vos données sont organisées sous la forme d'un tableau de tableaux de longueur variable; et ne nécessite pas une solution de génération de classe folle à la volée. En remarque, la génération de classes à la volée n'est pas intrinsèquement folle; c'est fou d'utiliser la génération de classes à la volée dans cette situation.

Faites ceci:

  1. Regardez vos données; il est organisé comme suit:
    1. premier: clé externe
    2. seconde: exactement une ligne contenant un nombre variable de tableau de clés internes séparées par des espaces.
    3. troisième: un certain nombre de lignes contenant des valeurs.
  2. Concevez une solution pour résoudre votre problème
    1. Lisez la clé externe. Utilisez cette valeur pour créer la partie clé externe de votre JSON.
    2. Lisez les clés intérieures. Stockez-les dans un tableau; utiliser LinkedList, pas ClownList (ArrayList).
    3. Faites ceci jusqu'à la prochaine ligne vide:
      1. Lire une ligne de valeurs.
      2. Ecrivez le JSON interne; utilisez les clés intérieures comme clés pour cela.
    4. Ignorez les lignes vides jusqu'à l'une des opérations suivantes:
      1. Si à la fin du fichier, écrivez la partie finale du JSON.
      2. Si vous lisez la clé externe suivante, passez à la ligne 2 (Lisez les clés internes) ci-dessus.
  3. Écrivez le code.

0 commentaires

0
votes

J'ai réalisé cela au lieu de créer les POJO avec une approche complexe. Il est préférable d'utiliser les Map et de les convertir en JSON en utilisant Jackson ObjectMapper . Publier pour d'autres personnes qui pensent que cela pourrait être une approche utile.

public String convert(Map<String, ? extends Metadata> metadataMap) {
        String output = "";
        Map<String, List<Map<String, String>>> finalMap = new HashMap<>();
        metadataMap.forEach((k, v) -> {

            List<Map<String, String>> datamap = new LinkedList<>();

            String key = k;
            String[] fields = v.getFields();
            List<String> lines = v.getLines();


            lines.forEach(line -> {
                if (line != null && !line.isEmpty() && !line.equals("null")) {
                    String[] fieldData = line.split("\t",-1);
                    Map<String, String> eachLineMap = new HashMap<>();
                    for (int index = 0; index < fields.length; index++) {
                        if (index < fieldData.length && (fieldData[index] != null && !fieldData[index].isEmpty())) {
                            eachLineMap.put(fields[index], fieldData[index]);
                        } else {
                            eachLineMap.put(fields[index], " ");
                        }
                        datamap.add(eachLineMap);
                    }
                }
            });
            finalMap.put(key, datamap);
        });

        try {
            output = new ObjectMapper().writeValueAsString(finalMap);
        }catch(JsonProcessingException e){
            e.printStackTrace();
        }

        return output;
    }


0 commentaires