Cómo procesar objetos con mapa para salida CSV
Tengo un conjunto de objetos a continuación, que necesito escribir en CSV:
public class OutputObject {
private String userId;
private Map<String, Object> behaviour;
}
El conjunto anterior puede tener un mapa con dos, tres o cuatro valores.
[
OutputObject1 [userId=11, behaviours={color=white, size=S, owner=Mr. A}],
OutputObject2 [userId=22, behaviours={color=black, isNew=true}],
OutputObject3 [userId=33, behaviours={color=green, size=L}]
]
Salida de CSV requerida:
userId, color, size, owner, isNew
11, white, S, Mr. A,
22, black, , , true
33, green, L, ,
Comencé con el siguiente fragmento para imprimir:
// Set<OutputObject> outputObjectSet already received.
JSONArray jsonArrayObject = new JSONArray(outputObjectSet);
String csvValue = CDL.toString(jsonArrayObject);
FileWriter fileWriter = new FileWriter(fileObject, true);
fileWriter.write(csvValue);
fileWriter.close();
Pero arriba está la creación de un csv de dos columnas con userId y comportamientos que imprimen todos los comportamientos del objeto del mapa . Cómo lograr el tipo de salida anterior.
Dado que el conjunto puede contener una gran cantidad de tales objetos, ¿cómo se puede hacer de manera eficiente?
Respuestas
El uso de JSONArray parece ser redundante aquí, podría implementar un método auxiliar para serializar OutputObjecten una cadena CSV, teniendo en cuenta que el orden de las columnas debe mantenerse:
public class CSVSerializer {
public static String transform(OutputObject obj) {
String[] fields = {"color", "size", "owner", "isNew"};
return Stream.concat(
Stream.of(obj.getUserId()),
Arrays.stream(fields)
.map(f -> obj.getBehaviour().get(f))
.map(v -> v == null ? "" : v.toString())
)
.collect(Collectors.joining(","));
}
}
String csv = outputObjectSet.stream()
.map(CSVSerializer::transform)
.collect(Collectors.joining("\n"));
// print csv contents
Esto debería funcionar bastante eficiente, incluso con un mayor número de objetos desde Java HashMapy Arrays.sort()las implementaciones son bastante rápido. Tenga en cuenta que esta implementación se basa en la common-textbiblioteca de Apache para escapar del contenido.
private static void outputCSV(List<OutputObject> objects, PrintStream output) {
AtomicInteger highestBehaviourIndex = new AtomicInteger();
HashMap<String, Integer> behaviourIndexMap = new HashMap<>();
// Give every behaviour an index
for (OutputObject object : objects) {
object.getBehaviour().forEach((name, value) -> behaviourIndexMap.computeIfAbsent(name, (unused) -> highestBehaviourIndex.getAndIncrement()));
}
String[] behaviours = new String[highestBehaviourIndex.get()];
behaviourIndexMap.forEach((name, index) -> {
behaviours[index] = name;
});
output.println("userId, " + String.join(", ", behaviours));
// Sort by ID
objects.sort(Comparator.comparingInt(OutputObject::getUserId));
for (OutputObject object : objects) {
// Print line
StringJoiner joiner = new StringJoiner(", ");
for (String behaviour : behaviours) {
joiner.add(StringEscapeUtils.escapeCsv(object.getBehaviour().getOrDefault(behaviour, "").toString()));
}
output.println(object.getUserId() + ", " + joiner.toString());
}
}