如何使用Jackson将CSV文件反序列化为Map<String, Foo>?
用Jackson将CSV反序列化为Map<String, Foo>
当然可以实现,下面提供两种实用方案:
方案一:先转List再转Map(简单易维护)
这是最直接的方式,先把CSV反序列化为List<Foo>,再通过Stream API转换成目标Map。
步骤1:完善Foo类
Jackson需要访问类的属性,所以要添加无参构造器和getter方法(也可以将字段设为public,但getter更符合Java规范):
class Foo { String x, y, z; // Jackson默认要求无参构造器 public Foo() {} // getter方法,用于后续提取Map的键 public String getX() { return x; } public String getY() { return y; } public String getZ() { return z; } // 可选:添加setter,或者用@JsonProperty标注字段来映射CSV表头 public void setX(String x) { this.x = x; } public void setY(String y) { this.y = y; } public void setZ(String z) { this.z = z; } }
步骤2:编写转换代码
假设CSV文件第一行是表头(比如x,y,z),代码示例:
import com.fasterxml.jackson.databind.MappingIterator; import com.fasterxml.jackson.dataformat.csv.CsvMapper; import com.fasterxml.jackson.dataformat.csv.CsvSchema; import java.io.File; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class CsvConverter { public static void main(String[] args) throws Exception { CsvMapper mapper = new CsvMapper(); // 配置CSV schema:使用第一行作为表头 CsvSchema schema = CsvSchema.emptySchema().withHeader(); // 反序列化为List<Foo> MappingIterator<Foo> iterator = mapper.readerFor(Foo.class).with(schema).readValues(new File("your-file.csv")); List<Foo> fooList = iterator.readAll(); // 转换为Map<String, Foo>,这里用x字段作为键 Map<String, Foo> fooMap = fooList.stream() .collect(Collectors.toMap(Foo::getX, foo -> foo)); // 处理重复键:如果CSV中存在相同的x值,需要指定冲突策略,比如保留第一个 // Map<String, Foo> fooMap = fooList.stream() // .collect(Collectors.toMap(Foo::getX, foo -> foo, (existing, newFoo) -> existing)); } }
方案二:自定义反序列化器(直接反序列化为Map)
如果不想先转List,可以自定义Jackson反序列化器,直接将CSV解析为Map。
步骤1:实现自定义反序列化器
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.dataformat.csv.CsvMapper; import com.fasterxml.jackson.dataformat.csv.CsvSchema; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; public class FooMapDeserializer extends JsonDeserializer<Map<String, Foo>> { @Override public Map<String, Foo> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { CsvMapper mapper = (CsvMapper) p.getCodec(); CsvSchema schema = CsvSchema.emptySchema().withHeader(); ObjectReader reader = mapper.readerFor(Foo.class).with(schema); List<Foo> fooList = reader.<Foo>readValues(p).readAll(); Map<String, Foo> fooMap = new HashMap<>(); // 用x字段作为键,可替换为y或z for (Foo foo : fooList) { fooMap.put(foo.getX(), foo); // 处理重复键可在此处添加判断逻辑 } return fooMap; } }
步骤2:注册并使用反序列化器
import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.dataformat.csv.CsvMapper; import com.fasterxml.jackson.dataformat.csv.CsvSchema; import java.io.File; import java.util.Map; public class CustomCsvConverter { public static void main(String[] args) throws Exception { CsvMapper mapper = new CsvMapper(); SimpleModule module = new SimpleModule(); // 注册自定义反序列化器 module.addDeserializer(Map.class, new FooMapDeserializer()); mapper.registerModule(module); CsvSchema schema = CsvSchema.emptySchema().withHeader(); // 直接反序列化为Map<String, Foo> Map<String, Foo> fooMap = mapper.readerFor(Map.class).with(schema).readValue(new File("your-file.csv")); } }
注意事项
- 确保CSV表头与Foo类的字段名匹配,如果表头名不同,可在字段上添加
@JsonProperty("表头名")来映射,比如@JsonProperty("id") String x; - 必须保证作为Map键的字段值唯一,否则需要处理重复键的冲突,避免抛出异常
- Foo类必须有无参构造器,Jackson默认依赖它来实例化对象,如果用带参构造器,需要添加
@JsonCreator和@JsonProperty标注构造参数
内容的提问来源于stack exchange,提问作者talfelt
相关产品推荐
相关产品推荐

