将扁平化的 CSV 转换为嵌套的 JSON

问题描述

我想从扁平化的 CSV 创建一个嵌套的 JSON:

CSV:

name address_city address_state
John Mumbai MH
John Bangalore KA
Bill Chennai TN

JSON:

[
 {
  "name": "John","address": [
              {
               "city": "Mumbai","state": "MH"
              },{
               "city": "Bangalore","state": "KA"
             }
            ]
 },{
  "name": "Bill","address": [
              {
               "city": "Chennai","state": "TN"
              }
            ]
 }
]

我正在使用带有 @nested 注释的 univocity 解析器,如下所示:

@nested(headerTransformer = AddresstypeTransformer.class,args = "address")
private Address address;

并且我得到如下的 JSON 输出,它具有地址对象而不是非常好的数组:

[
 {
  "name": "John","address": {
               "city": "Mumbai","state": "MH"
              }
 },{
  "name": "John","state": "MH"
             }
 },"address": {
               "city": "Chennai","state": "TN"
              }
 }
]

但是当我更改代码以将地址设为数组时:

@nested(headerTransformer = AddresstypeTransformer.class,args = "address")
private Address[] address;

我收到以下错误

Exception in thread "main" com.univocity.parsers.common.DataProcessingException: Unable to instantiate class '[Lcom.ss.beans.Address;'
Internal state when error was thrown: line=2,column=0,record=1,charIndex=58,headers=[id,name,address_city,address_state],

为什么@nested 注释不适用于数组/列表? 我怎么解决这个问题? 在不使用单义性的情况下,有没有其他方法可以解决这个问题?

PS:我是在遵循@Jeronimo Backes 在这文章中的回复后提出这个问题的: Convert CSV data into nested json objects using java library

解决方法

这是我的方法:

测试数据(在我的例子中,字段以制表符分隔):

name    address_city    address_state
John    Mumbai  MH
John    Bangalore   KA
Bill    Chennai TN

我使用的导入:

import com.google.gson.Gson;
import com.univocity.parsers.common.processor.BeanListProcessor;
import com.univocity.parsers.csv.CsvParser;
import com.univocity.parsers.csv.CsvParserSettings;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

处理代码:

//
// parse the source file into a list of SourceRecord beans:
//
Reader reader = new FileReader(new File("C:/tmp/univocity_demo.csv"),StandardCharsets.UTF_8);
BeanListProcessor<SourceRecord> processor = new BeanListProcessor<>(SourceRecord.class);
CsvParserSettings parserSettings = new CsvParserSettings();
parserSettings.getFormat().setDelimiter("\t"); // tab separated data
parserSettings.getFormat().setLineSeparator("\n");
parserSettings.setProcessor(processor);
CsvParser parser = new CsvParser(parserSettings);
parser.parse(reader);
List<SourceRecord> sourceRecords = processor.getBeans();

//
// process those SourceRecord objects into consolidated Name beans:
//
Map<String,Name> namesMap = new HashMap<>();
sourceRecords.forEach(sourceRecord -> {
    String sourceName = sourceRecord.getName();
    if (namesMap.containsKey(sourceName)) {
        namesMap.get(sourceName).getAddresses().add(sourceRecord.getAddress());
    } else {
        Name name = new Name();
        name.setName(sourceName);
        name.getAddresses().add(sourceRecord.getAddress());
        namesMap.put(sourceName,name);
    }
});

//
// convert to JSON:
///
Gson gson = new Gson();
String json = gson.toJson(namesMap.values());

SourceRecord bean 如下。请注意,除了基本的 @Nested 注释之外,我们不需要任何其他内容,此处:

public class SourceRecord {
    
    @Parsed(field = "name")
    private String name;
    
    @Nested
    private Address address;

    // getters/setters not shown

}

这是输出 NameAddress bean。注意我在 addresses bean 中使用了字段名称 address(不是 Name):

public class Name {
    
    private String name;
    
    private final List<Address> addresses = new ArrayList<>();

    // getters/setters not shown
}

还有 Address bean - 这既用于最终输出,也用于读取源文件(因此需要注释):

public class Address {
    
    @Parsed(field = "address_city")
    private String city;
    
    @Parsed(field = "address_state")
    private String state;

    // getters/setters not shown
    
}

最终的 JSON 是:

[{
    "name": "John","addresses": [{
        "city": "Mumbai","state": "MH"
    },{
        "city": "Bangalore","state": "KA"
    }]
},{
    "name": "Bill","addresses": [{
        "city": "Chennai","state": "TN"
    }]
}]