【问题标题】:Jackson CSV mapper doesn't work for custom schemaJackson CSV 映射器不适用于自定义架构
【发布时间】:2019-03-08 16:38:55
【问题描述】:

我有这个代码:

CsvSchema sema = CsvSchema.builder()
    .addColumn("name")
    .addColumn("year", CsvSchema.ColumnType.NUMBER)
    .build().withHeader();

ObjectReader reader = new CsvMapper().readerFor(JsonNode.class).with(sema);

JsonNode o = reader.readValue(new FileInputStream(new File("/path/to/test.csv")));
System.out.println(o);

test.csv 是:

test, year
1,    1

该代码应将CSV 解析为下一种格式的JSON

{"name":"1","year":1}

但我的输出是:

{"name":"1","year":"1"}

问题是:Jackson解析年份为String,但我在CSV Schema中配置的那一年是Number。有人知道是什么问题吗?

Jackson 版本是2.9.8,我也在2.7.1上试过

【问题讨论】:

  • 这个问题是否与Convert map to json string by json schema 重复?为什么你提出了几乎相同的两个问题?
  • 不,不是。第一个问题是如何将 csv 转换为 json 字符串,第二个问题是如何为 csv 使用 jackson 自定义模式。
  • 我的回答对您有帮助吗?

标签: java csv jackson


【解决方案1】:

经过几个小时的工作,我为您找到了解决方案。

我使用 FlexJson 来配置你的 json 的序列化。

    <!-- https://mvnrepository.com/artifact/net.sf.flexjson/flexjson -->
<dependency>
    <groupId>net.sf.flexjson</groupId>
    <artifactId>flexjson</artifactId>
    <version>2.0</version>
</dependency>

它不是很漂亮,但它确实有效。

我希望这对您有所帮助,我相信您可以改进此代码

public String generateJsonFromCSV(File csvFile, File schemaJson) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {

    //Get CsvSchema as Map
    Map<String, CsvSchema.ColumnType> map = getSchemaMapFromJson(schemaJson);
    //Create CsvSchema from CsvSchemaMap
    CsvSchema.Builder schemaBuilder = new CsvSchema.Builder();
    map.forEach(schemaBuilder::addColumn);
    CsvSchema schema = schemaBuilder.build();
    //read CSV
    CsvMapper csvMapper = new CsvMapper();
    MappingIterator<Map<?, ?>> mappingIterator = csvMapper.readerFor(Map.class).with(schema).readValues(csvFile);
    //Get configured JsonSerializer from CsvSchemaMap
    JSONSerializer jsonSerializer = getJsonSerializer(map);
    List<Map<?, ?>> lines = mappingIterator.readAll();
    //remove first line
    lines.remove(0);
    //serialize
    return jsonSerializer.deepSerialize(lines);
}

/**
 *
 * @param schemaMap mapping field to ColumnType
 * @return a configured JSONSerializer
 */
private JSONSerializer getJsonSerializer(Map<String, CsvSchema.ColumnType> schemaMap){
    Map<CsvSchema.ColumnType, Transformer> transformerMap = new EnumMap<>(CsvSchema.ColumnType.class);
    transformerMap.put(CsvSchema.ColumnType.STRING, new StringTransformer());
    transformerMap.put(CsvSchema.ColumnType.NUMBER, new NumberTransformer());
    JSONSerializer jsonSerializer = new JSONSerializer();
    for (Map.Entry<String, CsvSchema.ColumnType> columnTypeEntry : schemaMap.entrySet()) {
        jsonSerializer.transform(transformerMap.get(columnTypeEntry.getValue()),columnTypeEntry.getKey());
    }
    return jsonSerializer;
}

/**
 /**
 *
 * @param file JSON CsvSchema
 * @return fieldname ColumnType mapping
 * @throws ClassNotFoundException
 */
private Map<String, CsvSchema.ColumnType> getSchemaMapFromJson(File file) throws ClassNotFoundException {
    Map<String, String> schema = new JSONDeserializer<Map<String,String>>().deserialize(getResourceFileAsString(file.getName()));
    Map<String, CsvSchema.ColumnType> result = new HashMap<>(schema.size());
    for (Map.Entry<String, String> columnSchema : schema.entrySet()) {
        result.put(columnSchema.getKey(), CsvSchema.ColumnType.valueOf(columnSchema.getValue().toUpperCase()));
    }
    return result;
}

输出将是

[{"name":"foobar","year":1986},{"name":"testtest","year":777}]

【讨论】:

    【解决方案2】:

    要强制CsvMapper 使用给定类型,最好的方法是使用POJO。在这种情况下,CsvMapper 知道类型并在可能的情况下自动转换它。让我们创建一个示例数据:

    name,year
    1,1
    2,2
    3,2
    

    (在您的示例中,第一列名称是test,但我认为这只是一个错误,应该是name。)

    下面的应用展示了如何使用POJO解析CSV和写入JSON

    import com.fasterxml.jackson.annotation.JsonCreator;
    import com.fasterxml.jackson.annotation.JsonProperty;
    import com.fasterxml.jackson.databind.MappingIterator;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.ObjectReader;
    import com.fasterxml.jackson.databind.SerializationFeature;
    import com.fasterxml.jackson.dataformat.csv.CsvMapper;
    import com.fasterxml.jackson.dataformat.csv.CsvSchema;
    
    import java.io.File;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    
    public class CsvApp {
    
        public static void main(String[] args) throws IOException {
            File csvFile = new File("./resource/test.csv").getAbsoluteFile();
    
            CsvMapper csvMapper = new CsvMapper();
            CsvSchema bootstrapSchema = CsvSchema.emptySchema().withHeader();
            ObjectReader reader = csvMapper.readerFor(User.class).with(bootstrapSchema);
            MappingIterator<User> iterator = reader.readValues(csvFile);
    
            List<User> users = new ArrayList<>();
            iterator.forEachRemaining(users::add);
    
            System.out.println("Users read from CSV file:");
            users.forEach(System.out::println);
    
            System.out.println();
            System.out.println("Users in JSON format:");
            ObjectMapper jsonMapper = new ObjectMapper();
            jsonMapper.enable(SerializationFeature.INDENT_OUTPUT);
    
            System.out.println(jsonMapper.writeValueAsString(users));
        }
    }
    
    class User {
    
        private final String name;
        private final int year;
    
        @JsonCreator
        public User(@JsonProperty("name") String name, @JsonProperty("year") int year) {
            this.name = name;
            this.year = year;
        }
    
        public String getName() {
            return name;
        }
    
        public int getYear() {
            return year;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "name='" + name + '\'' +
                    ", year=" + year +
                    '}';
        }
    }
    

    上面的代码打印:

    Users read from CSV file:
    User{name='1', year=1}
    User{name='2', year=2}
    User{name='3', year=2}
    
    Users in JSON format:
    [ {
      "name" : "1",
      "year" : 1
    }, {
      "name" : "2",
      "year" : 2
    }, {
      "name" : "3",
      "year" : 2
    } ]
    

    【讨论】:

    • 是的,但我没有 pojo。我想使用 builder 创建自定义方案,而不是从 pojo 创建它
    猜你喜欢
    • 2018-02-18
    • 2012-07-08
    • 2021-05-02
    • 1970-01-01
    • 1970-01-01
    • 2015-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多