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(); }});
3 Réponses :
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; } }
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à.
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:
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; }
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é.