如何使用地图处理对象以获取 CSV 输出

How to process object with map for CSV output

我有一组以下对象,我需要将其写入 CSV:

public class OutputObject {
    private String userId;
    private Map<String, Object> behaviour;
}

以上集合可以有一个包含两个、三个或四个值的映射。

[
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}]
]

需要 CSV 输出:

userId, color, size, owner, isNew
11,     white,  S,   Mr. A,
22,     black,   ,        , true
33,     green,  L,        ,

我从下面的代码片段开始打印:

     // 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();

但上面是用 userIdbehaviours 创建一个两列的 csv 打印所有地图对象 behaviours.如何实现上述类型的输出。

由于集合可能包含大量此类对象,如何才能有效地完成此操作。

这应该会非常有效,即使有更多的对象,因为 Java 的 HashMapArrays.sort() 实现非常快。请注意,此实现依赖于 Apache 的 common-text-library 来转义内容。

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());
    }

}

在这里使用 JSONArray 似乎是多余的,您可以实现一个辅助方法将 OutputObject 序列化为 CSV 字符串,同时考虑到需要维护列的顺序:

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