Java වල Jackson Library එකෙන් JSON හදමු - Jackson JSON Tutorial SC Guide

Generating JSON with Jackson Library - The SC Guide

ආයුබෝවන් කට්ටියටම! කොහොමද ඉතින්? අද අපි කතා කරන්න යන්නේ මාරම වැදගත් මාතෘකාවක් ගැන, ඒ තමයි Java Object එකක් JSON වලට හරවගන්න හැටි. අද වෙනකොට applications හදනකොට, විශේෂයෙන්ම web services (APIs) එක්ක වැඩ කරනකොට JSON කියන්නේ නැතුවම බෑ වගේ දෙයක් වෙලා. මේක හරියට බත් කනකොට වතුර වගේ. ඉතින්, Java code එකක් ඇතුලෙන් මේක පහසුවෙන් කරගන්නේ කොහොමද? ඒකට තමයි Jackson Library එක එන්නේ. මේක Java developer කෙනෙක්ට නැතුවම බෑ කියන තරමටම වැදගත් tool එකක්.

අද අපි බලමු Jackson Library එක කොච්චර සෙට් එකක්ද කියලා. ඒ වගේම Java Objects JSON වලට serialize කරන්න මේක පාවිච්චි කරන්නේ කොහොමද කියලත්, ඒකේ තියෙන සුපිරි annotations ටිකකුත් උදාහරණත් එක්කම කියලා දෙන්නම්. ඒ නිසා අන්තිම වෙනකල්ම කියවන්න.

Jackson Library කියන්නේ මොකක්ද?

සරලව කිව්වොත්, Jackson කියන්නේ JSON data manipulate කරන්න පුළුවන් high-performance JSON processor එකක්. ඒ කියන්නේ Java object එකක් JSON එකකට හරවන්න (ඒකට කියන්නේ Serialization කියලා) වගේම, JSON එකක් Java object එකකට හරවන්නත් (ඒකට කියන්නේ Deserialization කියලා) මේක පාවිච්චි කරන්න පුළුවන්. අද අපි වැඩිපුර අවධානය යොමු කරන්නේ Serialization ගැන.

ඇයි Jackson? මේකට හේතු ගොඩක් තියෙනවා:

  • Speed: Jackson ගොඩක් වේගවත්. Large JSON files එක්ක වැඩ කරනකොට මේක ගොඩක් වාසියි.
  • Flexibility: මේකේ තියෙන annotations නිසා ඔබට JSON output එක ඕන විදියට customize කරගන්න පුළුවන්.
  • Robustness: ගොඩක් දේවල් automate කරනවා වගේම, error handling වගේ දේවලුත් හොඳට handle කරනවා.
  • Widely Adopted: Spring Framework එක වගේ ගොඩක් frameworks වල default JSON processor එක විදියට Jackson තමයි පාවිච්චි කරන්නේ. ඒ නිසා community support එකත් හොඳටම තියෙනවා.

Jackson Library එක Project එකට එකතු කරගනිමු (Dependencies)

ඔබ Maven හෝ Gradle පාවිච්චි කරනවා නම්, පහත dependencies add කරගන්න පුළුවන්:

Maven


<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.16.1</version> <!-- නවතම version එක බලන්න -->
</dependency>

Gradle


implementation 'com.fasterxml.jackson.core:jackson-databind:2.16.1' // නවතම version එක බලන්න

මූලික Serialization: ObjectMapper

Jackson Library එකේ ප්‍රධානම class එක තමයි ObjectMapper. මේක තමයි Java objects JSON වලට convert කරන්න පාවිච්චි කරන core class එක. මේකේ writeValueAsString() කියන method එකෙන් ඔබට ඕනෑම Java object එකක් JSON String එකක් විදියට ගන්න පුළුවන්.

අපි සරල Student class එකක් හදලා ඒක JSON වලට හරවලා බලමු:


// Student.java
public class Student {
    private String firstName;
    private String lastName;
    private int age;
    private String major;

    public Student(String firstName, String lastName, int age, String major) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
        this.major = major;
    }

    // Getters and Setters (අනිවාර්යයි Jackson වැඩ කරන්න)
    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getMajor() {
        return major;
    }

    public void setMajor(String major) {
        this.major = major;
    }
}

දැන් මේ Student object එක JSON වලට හරවමු:


// Main.java
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.JsonProcessingException;

public class Main {
    public static void main(String[] args) {
        ObjectMapper objectMapper = new ObjectMapper();

        Student student = new Student("Kamal", "Perera", 22, "Software Engineering");

        try {
            String jsonString = objectMapper.writeValueAsString(student);
            System.out.println(jsonString);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
    }
}

මේ code එක run කලාම ඔයාලට මෙහෙම output එකක් ලැබෙයි:


{"firstName":"Kamal","lastName":"Perera","age":22,"major":"Software Engineering"}

දැන් ඔයාලට පැහැදිලියි නේද? සරල Java object එකක් කොහොමද JSON string එකක් විදියට හැරෙන්නේ කියලා. ඒත් එක්කම මතක තියාගන්න, Jackson Library එකෙන් Java object එකක තියෙන getters (e.g., getFirstName()) පාවිච්චි කරලා තමයි JSON fields හදන්නේ. ඒ නිසා getters අනිවාර්යයෙන්ම තියෙන්න ඕනේ.

Jackson Annotations වලින් වැඩේ ලේසි කරගමු

Jackson වල තියෙන සුපිරිම feature එකක් තමයි Annotations. මේවා පාවිච්චි කරලා ඔබට JSON output එක ඕන විදියට customize කරගන්න පුළුවන්. හරියට ඔබේ කෑම එකට අලංකාරයක් එකතු කරනවා වගේ වැඩක් මේක.

@JsonProperty

ඔබට Java object එකක field එකක නම JSON output එකේදී වෙනස් කරන්න ඕනේ නම් @JsonProperty annotation එක පාවිච්චි කරන්න පුළුවන්. ඒ වගේම setter/getter method එකකටත් මේක දාන්න පුළුවන්.


import com.fasterxml.jackson.annotation.JsonProperty;

public class Student {
    @JsonProperty("first_name") // 'firstName' වෙනුවට 'first_name' විදියට serialize වෙනවා
    private String firstName;
    private String lastName;
    private int age;
    private String major;

    // Constructor, Getters, Setters
    public Student(String firstName, String lastName, int age, String major) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
        this.major = major;
    }

    public String getFirstName() { return firstName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public String getLastName() { return lastName; }
    public void setLastName(String lastName) { this.lastName = lastName; }
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }
    public String getMajor() { return major; }
    public void setMajor(String major) { this.major = major; }
}

Output එක:


{"first_name":"Kamal","lastName":"Perera","age":22,"major":"Software Engineering"}

@JsonInclude

සමහර වෙලාවට අපිට ඕනේ JSON output එකේ null values තියෙන fields, නැත්නම් empty lists/maps වගේ දේවල් අයින් කරන්න. ඒකට තමයි @JsonInclude annotation එක පාවිච්චි කරන්නේ. මේක class level එකේදී හෝ field level එකේදී පාවිච්චි කරන්න පුළුවන්.

උදාහරණයක් විදියට, major කියන field එක null නම්, ඒක JSON එකේ පෙන්නන්න එපා කියලා කියමු:


import com.fasterxml.jackson.annotation.JsonInclude;

// Class level එකේදීත් දාන්න පුළුවන්
//@JsonInclude(JsonInclude.Include.NON_NULL)
public class Student {
    private String firstName;
    private String lastName;
    private int age;

    @JsonInclude(JsonInclude.Include.NON_NULL) // මේ field එක null නම් JSON එකට add වෙන්නේ නෑ
    private String major;

    public Student(String firstName, String lastName, int age, String major) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
        this.major = major;
    }

    // Getters and Setters
    public String getFirstName() { return firstName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public String getLastName() { return lastName; }
    public void setLastName(String lastName) { this.lastName = lastName; }
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }
    public String getMajor() { return major; }
    public void setMajor(String major) { this.major = major; }
}

දැන් අපි major එක null කරලා බලමු:


// Main.java
// ... (ඉහත Main class එකේ වගේම)
Student studentWithoutMajor = new Student("Nimal", "Silva", 23, null);
String jsonStringWithoutMajor = objectMapper.writeValueAsString(studentWithoutMajor);
System.out.println(jsonStringWithoutMajor);

Output එක:


{"firstName":"Nimal","lastName":"Silva","age":23}

දැන් බලන්න major field එක නැති බව. JsonInclude.Include.NON_EMPTY, NON_DEFAULT වගේ අනිත් options ගැනත් ඔබට හොයලා බලන්න පුළුවන්.

@JsonFormat

Dates එක්ක වැඩ කරනකොට මේක ගොඩක් වැදගත්. JSON වලට date එකක් serialize කරනකොට ඒකේ format එක specify කරන්න මේක පාවිච්චි කරන්න පුළුවන්.


import com.fasterxml.jackson.annotation.JsonFormat;
import java.util.Date;

public class Student {
    private String firstName;
    private String lastName;
    private int age;
    private String major;

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
    private Date registrationDate;

    public Student(String firstName, String lastName, int age, String major, Date registrationDate) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
        this.major = major;
        this.registrationDate = registrationDate;
    }

    // Getters and Setters
    public String getFirstName() { return firstName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public String getLastName() { return lastName; }
    public void setLastName(String lastName) { this.lastName = lastName; }
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }
    public String getMajor() { return major; }
    public void setMajor(String major) { this.major = major; }
    public Date getRegistrationDate() { return registrationDate; }
    public void setRegistrationDate(Date registrationDate) { this.registrationDate = registrationDate; }
}

// Main.java
// ...
Student studentWithDate = new Student("Saman", "Kumara", 20, "Physics", new Date());
String jsonStringWithDate = objectMapper.writeValueAsString(studentWithDate);
System.out.println(jsonStringWithDate);

Output එක:


{"firstName":"Saman","lastName":"Kumara","age":20,"major":"Physics","registrationDate":"2024-03-20"}

(Date එක ඔබේ system එකේ date එක අනුව වෙනස් වෙයි)

@JsonIgnore

සමහර වෙලාවට ඔබට ඕනේ නැති field එකක් JSON output එකට එකතු වෙන්නේ නැති වෙන්න. ඒකට තමයි @JsonIgnore annotation එක පාවිච්චි කරන්නේ.


import com.fasterxml.jackson.annotation.JsonIgnore;

public class Student {
    private String firstName;
    private String lastName;
    private int age;
    private String major;

    @JsonIgnore // මේ field එක JSON එකට serialize වෙන්නේ නෑ
    private String password;

    public Student(String firstName, String lastName, int age, String major, String password) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
        this.major = major;
        this.password = password;
    }

    // Getters and Setters
    public String getFirstName() { return firstName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public String getLastName() { return lastName; }
    public void setLastName(String lastName) { this.lastName = lastName; }
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }
    public String getMajor() { return major; }
    public void setMajor(String major) { this.major = major; }
    public String getPassword() { return password; }
    public void setPassword(String password) { this.password = password; }
}

// Main.java
// ...
Student studentWithPassword = new Student("Chathuranga", "Silva", 25, "IT", "mysecretpassword");
String jsonStringWithPassword = objectMapper.writeValueAsString(studentWithPassword);
System.out.println(jsonStringWithPassword);

Output එක:


{"firstName":"Chathuranga","lastName":"Silva","age":25,"major":"IT"}

බලන්න, password field එක output එකේ නැහැ.

ප්‍රායෝගික උදාහරණ සහ හොඳ පුරුදු

දැන් අපි ටිකක් complex scenario එකක් බලමු. Course එකක් ඇතුලේ students ලා ගොඩක් ඉන්නවා වගේ situation එකක්:


import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
import java.util.ArrayList;

// Student.java (ඉහත Annotations එක්කම පාවිච්චි කරන්න පුළුවන්)
// ... (JsonInclude, JsonProperty annotation ටිකත් student class එකට එකතු කරන්න)

// Course.java
public class Course {
    private String courseId;
    private String courseName;
    private List<Student> students; // Student objects list එකක්

    public Course(String courseId, String courseName, List<Student> students) {
        this.courseId = courseId;
        this.courseName = courseName;
        this.students = students;
    }

    // Getters and Setters
    public String getCourseId() { return courseId; }
    public void setCourseId(String courseId) { this.courseId = courseId; }
    public String getCourseName() { return courseName; }
    public void setCourseName(String courseName) { this.courseName = courseName; }
    public List<Student> getStudents() { return students; }
    public void setStudents(List<Student> students) { this.students = students; }
}

// Main.java
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.SerializationFeature;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Date;

public class Main {
    public static void main(String[] args) {
        ObjectMapper objectMapper = new ObjectMapper();
        // JSON output එක කියවන්න පහසු වෙන්න pretty print කරන්න
        objectMapper.enable(SerializationFeature.INDENT_OUTPUT);

        Student student1 = new Student("Kamal", "Perera", 22, "Software Engineering", new Date());
        // password එකක් දීලා බලන්න, නමුත් @JsonIgnore නිසා serialize වෙන්නේ නෑ
        Student student2 = new Student("Nimal", "Silva", 23, null, "secret_pass_123");
        student2.setMajor("Data Science"); // Major එක null වුණත් පස්සේ set කරන නිසා serialize වෙයි

        List<Student> studentList = Arrays.asList(student1, student2);

        Course softwareCourse = new Course("CS101", "Introduction to Software Engineering", studentList);

        try {
            String jsonString = objectMapper.writeValueAsString(softwareCourse);
            System.out.println(jsonString);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
    }
}

Output එක (pretty-printed):


{
  "courseId" : "CS101",
  "courseName" : "Introduction to Software Engineering",
  "students" : [ {
    "firstName" : "Kamal",
    "lastName" : "Perera",
    "age" : 22,
    "major" : "Software Engineering",
    "registrationDate" : "2024-03-20"
  }, {
    "firstName" : "Nimal",
    "lastName" : "Silva",
    "age" : 23,
    "major" : "Data Science"
  } ]
}

(registrationDate ඔබේ run කරන දවසේ date එකට අනුව වෙනස් වෙයි)

හොඳ පුරුදු (Best Practices)

  • ObjectMapper Instance Reuse කරන්න: ObjectMapper object එකක් හදන එක ටිකක් resource-intensive වැඩක්. ඒ නිසා පුළුවන් නම් මේක එකපාරක් හදලා application එක පුරාම reuse කරන්න.
  • Error Handling: Jackson methods වලට JsonProcessingException එකක් throw කරන්න පුළුවන්. ඒ නිසා අනිවාර්යයෙන්ම try-catch block එකක් ඇතුලේ වැඩේ කරන්න.
  • Pretty Printing: Development කරනකොට JSON output එක කියවන්න පහසු වෙන්න objectMapper.enable(SerializationFeature.INDENT_OUTPUT); වගේ feature එකක් enable කරගන්න. Production වලදී මේක අවශ්‍ය වෙන්නේ නෑ.
  • Security: සංවේදී දත්ත (passwords වගේ) JSON වලට serialize කරනවා නම් @JsonIgnore වගේ annotations අනිවාර්යයෙන්ම පාවිච්චි කරන්න.

නිගමනය

ඉතින්, අද අපි Jackson Library එක පාවිච්චි කරලා Java Objects JSON වලට serialize කරන්නේ කොහොමද කියලා කතා කළා. ObjectMapper class එකෙන් පටන් අරන්, @JsonProperty, @JsonInclude, @JsonFormat, @JsonIgnore වගේ ප්‍රයෝජනවත් annotations ගැනත් අපි ඉගෙනගත්තා. Jackson කියන්නේ Java developers ලට නැතුවම බැරි tool එකක්, මොකද JSON handling කියන දේ මේකෙන් මාර විදියට simplify කරනවා.

දැන් ඔබට Jackson වල මූලික දැනුමක් තියෙනවා. ඉතින් අනිවාර්යයෙන්ම මේක ඔබේ project වලට apply කරලා බලන්න. ඒ වගේම Jackson වල තව ගොඩක් annotations සහ advanced features තියෙනවා. (උදාහරණයක් විදියට Deserialization ගැන අපි ඊලඟ ලිපියකින් කතා කරමු!).

ඔබේ අදහස් පහලින් comment කරන්න. මේකෙන් දෙයක් ඉගෙනගත්තා නම් share කරන්න අමතක කරන්න එපා! ඊලඟ SC Guide එකෙන් හම්බවෙමු!