Como processar objeto com mapa para saída CSV
Tenho um conjunto de objetos abaixo, que preciso escrever em CSV:
public class OutputObject {
private String userId;
private Map<String, Object> behaviour;
}
O conjunto acima pode ter um mapa com dois, três ou quatro 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}]
]
Saída de CSV necessária:
userId, color, size, owner, isNew
11, white, S, Mr. A,
22, black, , , true
33, green, L, ,
Comecei com o snippet abaixo 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();
Mas acima está criando um csv de duas colunas com userId e comportamentos de impressão de todos os comportamentos do objeto de mapa . Como obter o tipo de saída acima.
Como o conjunto pode conter um grande número de tais objetos, como isso pode ser feito de forma eficiente.
Respostas
Usar JSONArray parece ser redundante aqui, você pode implementar um método auxiliar para serializar OutputObjectem uma string CSV, levando em consideração que a ordem das colunas precisa ser mantida:
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
Isso deve funcionar de forma bastante eficiente, mesmo com um número maior de objetos, uma vez que Java HashMape Arrays.sort()implementações são muito rápidas. Observe que essa implementação depende da common-text-library do Apache para escapar do conteúdo.
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());
}
}