Monday, August 9, 2021

How to Read or Parse CSV files with Header in Java using Jackson - Example Tutorial

Hello guys, today I am going to show you how to read a CSV file in Java with a simple example of Jackson API. For a long time, I only knew that Jackson can be used to parse JSON but later realized that you can also use it to parse or read CSV files in Java. The Jackson DataFormat CSV library allows you to read a CSV file in just a couple of lines and it's really powerful and feature-rich. For example, you can read a CSV file with a header or without a header. You can even read a tab-delimited text file like CSV. It's also highly configurable and allows you to define a columns separator to read a text file with any delimiter. You can even use it to directly convert the CSV data into Java objects, just like we do while reading JSON String in Java.

For a long time, I was like a DIY guy, I mean, I would like to code everything I need and that's why I always have that big Utility class in my every Java project but things changed after reading Effective Java.

The book is full of best practices and great practical advice from Joshua Bloch, author of the Java Collection framework, and many key classes on java.lang package. In one of the items on Effective Java, he advised that you should always use libraries for the common task instead of re-inventing the wheel and from then I have not looked back.

The same is true for reading or parsing CSV files in Java. If you look at my earlier solution, it wasn't a production-ready solution. I have demonstrated how you can use BufferedReader to read a CSV file but it doesn't handle many corner cases like carriage return in values, any field protected by quotes, etc. It was also not optimized for performance.

When you use a tried and tested and proven library you get all these benefits by default and that's the main reason I ask Java developers to get themselves familiar with essential and useful Java libraries. I have shared around 20 most useful Java libraries earlier and I am encouraging my readers to suggest as well, if you haven't read that article yet, you should read it now.





How to parse a CSV file with column header in Java using Jackson

Anyway, let's come back to the topic. In this article, I'll show you how to read a CSV file using the Jackson library in Java.

Suppose we have the following CSV file which contains the title, author, and price of popular online courses for Java developers.

Title,Author,Price
REST With Spring,Eugen Paraschiv, 290
Learn Spring Security,Baeldung,290
Complete Java MasterClass,Udemy,200

Actually, I was tired of the book examples I always use, so, I used online courses this time, but they are some of the really best courses and if you are interested in Java then The Complete Java Masterclass is one of the best courses to start with, feel free to join them.

How to read a CSV file in Java using Jackson with Example


Now, to represent this data in our Java application, I have created a class called OnlineCourse, which looks like below:

class OnlineCourse{
  private String title;
  private String author;
  private int price; 
 
  ...
 
  public OnlineCourse(){
     // no argument constructor required by Jackson
  }
 
}

I have omitted other constructors, getter, and setter for readability and you can also do if you use the Lombok library. It has both pros and cons but that's a topic for another day.

Just remember that Jackson uses reflection hence a default no-argument constructor is mandatory in your class.

Now, we'll write code to read this CSV file in Java and copy the objects into ArrayList or just print them into the console.


This is what you need to read the CSV file using Jackson:

CsvMapper csvMapper = new CsvMapper();
CsvSchema schema = CsvSchema.emptySchema().withHeader(); 
 
ObjectReader oReader = csvMapper.reader(OnlineCourse.class).with(schema);
try (Reader reader = new FileReader("file.txt")) {
    MappingIterator<OnlineCourse> mi = oReader.readValues(reader);
    while (mi.hasNext()) {
      System.out.println(current);
    }
}

A CsvMapper is a class that maps data from CSV to Java objects while CsvSchema defines Schema as whether CSV has a header or not. You can also specify if a file is comma-separated or tab-delimited, which we'll see in the next article.

This will print the following in the console:
EBook [title=REST With Spring, author=Eugen Paraschiv, price=290]
EBook [title=Learn Spring Security, author=Baeldung, price=290]
EBook [title=Complete Java MasterClass, author=Udemy, price=200

This means our program successfully reads the CSV file. If you want you can store the objects into a List or Map as well as we'll do in our sample program. Btw, if you are not familiar with essential Collection classes like those then please see Java Fundamentals: Collections course by Richard Warburton on Pluralsight.

How to parse a CSV file with column header in Java using Jackson



Jackson Example to Read CSV File in Java 

You can configure Jackson to use the first line of CSV document to get the names (no types) for Schema. A schema object is required to ensure correct ordering of columns; schema instances are immutable and fully reusable (as are ObjectWriter instances).

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
 
import com.fasterxml.jackson.databind.MappingIterator;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
import com.fasterxml.jackson.dataformat.csv.CsvSchema;
 
/*
* Java Program to iterate over JSONObject of json-simple
*/
public class JacksonTest {
 
public static void main(String args[]) throws FileNotFoundException, IOException { 
    CsvMapper csvMapper = new CsvMapper();
    CsvSchema schema = CsvSchema.emptySchema().withHeader(); 
 
    ObjectReader oReader = csvMapper.reader(OnlineCourse.class).with(schema);
    List<OnlineCourse> courses = new ArrayList<>();
 
     try (Reader reader = new FileReader("file.txt")) {
           MappingIterator<OnlineCourse> mi = oReader.readValues(reader);
           while (mi.hasNext()) {
                 OnlineCourse current = mi.next();
                 courses.add(current);
                 System.out.println(current);
      }
}
 
   System.out.println("number of courses into list: " + courses.size());
 
}
 
}
 
 
class OnlineCourse{
private String title;
private String author;
private int price; 
 
public OnlineCourse(){
    // no argument constructor required by Jackson
}
 
public OnlineCourse(String title, String author, int price) {
    this.title = title;
    this.author = author;
     this.price = price;
}
 
public String getTitle() {
    return title;
}
 
public String getAuthor() {
   return author;
}
 
public int getPrice() {
   return price;
}
 
 
public void setTitle(String title) {
   this.title = title;
}
 
public void setAuthor(String author) {
   this.author = author;
}
 
public void setPrice(int price) {
   this.price = price;
}
 
 
@Override
public String toString() {
     return "EBook [title=" + title + ", author=" + author + ", price="
                              + price + "]";
  } 
 
}
 
Output:
EBook [title=REST With Spring, author=Eugen Paraschiv, price=290]
EBook [title=Learn Spring Security, author=Baeldung, price=290]
EBook [title=Complete Java MasterClass, author=Udemy, price=200]
number of courses into list: 3

You can see that we have successfully converted a CSV file into a bunch of Java objects. Each line which is nothing but a CSV String now represents a Java object. If you want to learn more about Jackson, I suggest you take a look at the JSON with Java APIs and REST Web Services course on Udemy.

JSON with Java APIs and REST Web Services course udemy



Common Errors

Nothing is easy in this world and if you do something the first time, you are bound to get some obstacles and that's true with parsing a CSV file using Jackson as well.

Exception in thread "main" java.lang.VerifyError: Cannot inherit from final class
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at com.fasterxml.jackson.dataformat.csv.CsvMapper.<init>(CsvMapper.java:39)
at JacksonTest.main(JacksonTest.java:25)


Cause and Solution

This error puzzled me a little bit but I quickly realize that it has something to do with incompatible versions because an error is coming from the Jackson library itself. That was indeed the case, I was using jackson-dataformat-csv-2.9.jar but I had jackson-core-2.2.3.jar in my classpath. After using jackson-dataformat-csv-2.2.jar the error goes away automatically.

In short, make sure that all your Jackson components have the same minor version: with dataformat-csv 2.9.jar you MUST use jackson-core and jackson-databind of 2.9 as well.

Btw, it's better to use tools like Maven or Gradle which will do dependency management for you, instead of you downloading Jackson JAR manually.





Another error:
at [Source: java.io.FileReader@3b81a1bc; line: 2, column: 16] (through reference chain: OnlineCourse["Title"])
at com.fasterxml.jackson.databind.MappingIterator.next(MappingIterator.java:122)
at JacksonTest.main(JacksonTest.java:34)
Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "Title" (class OnlineCourse), not marked as ignorable (3 known properties: , "title", "price", "author"])
at [Source: java.io.FileReader@3b81a1bc; line: 2, column: 16] (through reference chain: OnlineCourse["Title"])

at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:79)
at com.fasterxml.jackson.databind.DeserializationContext.reportUnknownProperty(DeserializationContext.java:555)
at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:708)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1160)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:315)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:121)
at com.fasterxml.jackson.databind.MappingIterator.nextValue(MappingIterator.java:189)
at com.fasterxml.jackson.databind.MappingIterator.next(MappingIterator.java:120)
... 1 more


Reason:
When you parse CSV data using Jackson it's like parsing JSON which means your property should exactly match with the fields in your class. It's also case-sensitive. In our case, the field in the class is called "title" while in the CSV file header is called "Title" hence error, after sorting that this error has gone.


That's all about how to read a CSV file in Java using Jackson DataFormat CSV API. You can easily it's very easy for that. The Jackson Library is both powerful and feature-rich and allows you to configure CSVSchema in multiple ways which means you can read CSV files with header (first line), without header, values with quotes in it, and line break on values.

You can even read a tab-delimited text file using this library. I'll share some more examples of the Jackson DataFormat CSV library but till then do whatever you want to do but don't trouble your mother :-)


Other JSON tutorials for Java programmers
  • 3 ways to convert String to JSON object in Java? (tutorial)
  • 5 JSON libraries Java JEE Programmer should know (list)
  • Why use Spring to create REST API in Java? (article)
  • How to parse JSON with the date field in Java using Jackson? (tutorial)
  • How to convert JSON array to String array in Java using Gson? (tutorial)
  • 6 Courses to learn Spring in depth (courses)
  • How to Escape JSON String in Eclipse (tips)
  • How to ignore Unknown properties while parsing JSON in Java? (tutorial)
  • How to download the Jackson library for JSON parsing? (tutorial)
  • How to convert a JSON array to String Array in Java? (tutorial)
  • How to parse a large JSON file using Jackson Streaming API? (example)
  • How to use Google Protocol Buffer (protobuf) in Java? (tutorial)
  • Top 5 courses to learn Spring boot in-depth (courses)
  • Top 10 RESTful Web Service Interview Questions (see here)
  • What is the purpose of different HTTP methods in REST? (see here)
  • 5 Courses to learn RESTFul Web Services in Java? (courses)

Thanks for reading this article so far. If you like this Jackson tutorial to parse CSV file in Java then please share with your friends and colleagues. If you have any questions or feedback then please drop a note. 

P. S. - If you are new to Java and looking for some free courses to start with then you can also check out this list of free Java Courses for Beginners. Join them before they expire.

9 comments:

  1. Thank so much for solved err solution

    ReplyDelete
  2. my jackson version is need Header in csv,

    ReplyDelete
  3. I have given pojo properties same as CSV file, but facing unrecognized field error

    ReplyDelete
  4. I have also given pojo properties same as CSV file, but facing unrecognized field error, the fields showing as unrecognized dont even exist anymore anywhere

    ReplyDelete
    Replies
    1. check if you are running old class file, sometime the class didn't compile or maven didn't remove them.

      Delete
  5. HI Javin,
    Can we use the library to populate values inside other objects?
    for instance there is an variable CustomClass custom;
    so keeping the feild name in CSV string as custom.feildName does this work?

    ReplyDelete
    Replies
    1. Hello Devang, I haven't researched but yes it looks possible. For example, you can have a field called full name which is not present in CSV but you can initialize it by using firstname + lastname which is present in CSV. Try to look at documentation or code in IDE to see some hints

      Delete
  6. ava.lang.NoSuchMethodError: 'com.fasterxml.jackson.core.json.JsonReadContext com.fasterxml.jackson.core.json.JsonReadContext.createRootContext()'

    ReplyDelete
  7. Hi..how to create class if my csv file headers varies from file to file

    ReplyDelete

Feel free to comment, ask questions if you have any doubt.