【问题标题】:how to read a csv to a nested json with jackson java如何使用jackson java将csv读取到嵌套的json
【发布时间】:2020-08-05 18:08:38
【问题描述】:

我有这种类型的 csv:

metric,value,date
temp_a,622.0,1477895624866
temp_a,-3.0,1477916224866
temp_a,365.0,1477917224866
temp_b,861.0,1477895624866
temp_b,767.0,1477917224866

我想使用 java jackson 将其转换为 json 但不是任何 json;它需要是这样的:

[
  {
    "metric":"temp_a",
    "datapoints":[
      [622, 1477895624866],
      [-3, 1477916224866],
      [365, 1477917224866]
    ]
  },
  {
    "metric":"temp_b",
    "datapoints":[
      [861, 1477895624866],
      [767, 1477917224866]
    ]
  }
]

其中 dataponits 是一个数组,其中包含 csv 中的值和日期。

我已经设法使用杰克逊得到这个结果:

{metric=temp_a, value=622.0, date=1477895624866}
{metric=temp_a, value=-3.0, date=1477916224866}
{metric=temp_a, value=365.0, date=1477917224866}
{metric=temp_b, value=861.0, date=1477895624866}
{metric=temp_b, value=767.0, date=1477917224866}

但这不是我想要的,jackson doc 对我来说有点难以理解和使用,也许这可以通过 Pojos 或注释来实现,但我无法理解它们,我找不到如何做一个嵌套的json。

如果我能在这方面做得更好,那么杰克逊请告诉我。 谢谢你的帮助。

【问题讨论】:

    标签: java json csv jackson nested


    【解决方案1】:

    您不必总是将CSV 反序列化为POJO 结构并实现自定义序列化程序。在这种情况下,您还可以:

    • CSV反序列化为Map
    • Map 中的元素分组为metric -> [[...], [...]] 表单
    • 将上面的Map 转换为Map 的另一种形式
    • Map 序列化为JSON

    示例代码如下所示:

    import com.fasterxml.jackson.core.util.DefaultIndenter;
    import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
    import com.fasterxml.jackson.databind.MappingIterator;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.SerializationFeature;
    import com.fasterxml.jackson.databind.json.JsonMapper;
    import com.fasterxml.jackson.dataformat.csv.CsvMapper;
    import com.fasterxml.jackson.dataformat.csv.CsvSchema;
    
    import java.io.File;
    import java.math.BigDecimal;
    import java.util.Arrays;
    import java.util.LinkedHashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.Objects;
    import java.util.Spliterator;
    import java.util.Spliterators;
    import java.util.stream.Collectors;
    import java.util.stream.Stream;
    import java.util.stream.StreamSupport;
    
    public class CsvApp {
    
        public static void main(String[] args) throws Exception {
            File csvFile = new File("./resource/test.csv").getAbsoluteFile();
    
            CsvMapper csvMapper = CsvMapper.builder().build();
            MappingIterator<Map> rows = csvMapper
                    .readerWithSchemaFor(Map.class)
                    .with(CsvSchema.emptySchema().withHeader())
                    .readValues(csvFile);
    
            DataConverter converter = new DataConverter();
            List<Map<String, Object>> result = converter.toMetricDataPoints(rows);
    
            ObjectMapper jsonMapper = JsonMapper.builder()
                    .enable(SerializationFeature.INDENT_OUTPUT)
                    .build();
    
            jsonMapper.writeValue(System.out, result);
        }
    
    }
    
    class DataConverter {
    
        public List<Map<String, Object>> toMetricDataPoints(MappingIterator<Map> rows) {
            return toStream(rows)
                //group by metric -> [value, date]
                .collect(Collectors.groupingBy(map -> map.get("metric"),
                    Collectors.mapping(map -> Arrays.asList(toNumber(map.get("value")), toNumber(map.get("date"))),
                        Collectors.toList())))
                .entrySet().stream()
                // convert to Map: metric + datapoints
                .map(entry -> {
                    Map<String, Object> res = new LinkedHashMap<>(4);
                    res.put("metric", entry.getKey());
                    res.put("datapoints", entry.getValue());
    
                    return res;
                }).collect(Collectors.toList());
        }
    
        private Stream<Map> toStream(MappingIterator<Map> rowIterator) {
            return StreamSupport.stream(Spliterators.spliteratorUnknownSize(rowIterator, Spliterator.ORDERED), false);
        }
    
        private long toNumber(Object value) {
            return new BigDecimal(Objects.toString(value, "0")).longValue();
        }
    }
    

    上面的代码打印:

    [ {
      "metric" : "temp_a",
      "datapoints" : [ [ 622, 1477895624866 ], [ -3, 1477916224866 ], [ 365, 1477917224866 ] ]
    }, {
      "metric" : "temp_b",
      "datapoints" : [ [ 861, 1477895624866 ], [ 767, 1477917224866 ] ]
    } ]
    

    如您所见,我们仅使用了基本的 Jackson 功能,其余的操作我们使用 Java 8 API 实现的数据。

    另见:

    1. Directly convert CSV file to JSON file using the Jackson library
    2. How to convert an iterator to a stream?
    3. Jackson JSON Deserialization: array elements in each line

    【讨论】:

    • 非常感谢您的帮助,这正是我正在寻找的东西:) 但我只是想知道这是否适用于大型 csv 文件?这是处理大型 csv 文件的最佳方式吗?
    • @feiz,这取决于您所说的large file 是什么意思。如果文件小于几兆字节,上面的代码应该可以工作。上面的代码将所有数据加载到内存并在内存中执行操作。因此,在您没有收到OutOfMemoryError 之前,您可以使用上述测试。在其他情况下,您需要了解Streaming API
    • 好的,我明白了,在这种情况下,我认为这段代码可以正常工作,非常感谢:)
    猜你喜欢
    • 1970-01-01
    • 2023-03-14
    • 1970-01-01
    • 2015-07-08
    • 2013-11-19
    • 2018-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多