为了账号安全,请及时绑定邮箱和手机立即绑定

Jackson CSV 映射器不适用于自定义架构

Jackson CSV 映射器不适用于自定义架构

至尊宝的传说 2022-07-27 11:34:20
我有这个代码: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, year1,    1该代码应解析CSV为JSON下一种格式:{"name":"1","year":1}但我的输出是:{"name":"1","year":"1"}问题是:Jackson将年份解析为String,但我在CSV Schema那一年配置的是Number. 有人知道是什么问题吗?杰克逊版本是2.9.8,我也试过了2.7.1
查看完整描述

2 回答

?
白猪掌柜的

TA贡献1893条经验 获得超10个赞

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


我使用 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}]


查看完整回答
反对 回复 2022-07-27
?
天涯尽头无女友

TA贡献1831条经验 获得超9个赞

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


name,year

1,1

2,2

3,2

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


下面的应用程序显示了如何解析CSV和编写JSON使用POJO:


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

} ]


查看完整回答
反对 回复 2022-07-27
  • 2 回答
  • 0 关注
  • 91 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信