1. Jackson-a giriş
Java-tətbiqi ilə xarici dünya arasında məlumat mübadiləsini çox vaxt belə bir vəzifə kimi təsəvvür etmək olar: “Java obyektini JSON-a necə çevirmək — və əksinə?” Əlbəttə, öz parserinizi String.split və müntəzəm ifadələr (regex) ilə yaza bilərsiniz (və hətta əziyyətdən “zövq” ala bilərsiniz), amma real layihələrdə bunu heç kim etmir.
Java-da JSON ilə işləmək üçün bir neçə məşhur kitabxana var. Onların ən önəmlilərindən biri — Jackson-dır. O, o qədər populyardır ki, Spring Boot-a daxildir və bir çox digər framework və kitabxanalarda istifadə olunur.
Jackson nədir?
Jackson — seriyalaşdırma (Java obyektinin JSON-a çevrilməsi) və deserializasiya (əks əməliyyat) üçün güclü və çevik kitabxanadır. O, bir neçə moduldən ibarətdir, lakin işlərin 90%-i üçün sizə iki modul lazım olacaq:
- jackson-core — nüvə, aşağı səviyyəli parser.
- jackson-databind — yüksək səviyyəli modul, Java obyektlərini JSON-a və əksinə çevirməyi bacarır.
Başlanğıc üçün bilməli olduğunuz hər şey: əgər ObjectMapper sinfini görürsünüzsə — bu, Jackson-dır.
Jackson-u Java-da JSON ilə iş üçün de-fakto standart hesab edirlər, çünki o, sadəliyi və gücü birləşdirir. Məlumatları seriyalaşdırmaq və ya deserializasiya etmək üçün cəmi bir neçə sətir kod kifayətdir, bu da kitabxananı yeni başlayanlar üçün belə rahat edir. Eyni zamanda imkanları bazaya məhdudlaşmır: annotasiyalar və çoxsaylı parametrlər sayəsində kolleksiyalar, iç-içə obyektlər və müxtəlif formatlarda tarixlər də daxil olmaqla, məlumatların obyektlərə və əksinə necə çevriləcəyini çevik şəkildə idarə etmək mümkündür.
Üstəlik, Jackson çox sürətli və qənaətcil işləyir ki, bu da böyük həcmli məlumatlarla real layihələr üçün kritikdir. Kitabxana müəllifləri Java-nın yeni versiyalarını və JSON standartındakı aktual dəyişiklikləri dəstəkləyirlər — Jackson həm sadə tətbiqlər, həm də iri korporativ sistemlər üçün etibarlı seçim olaraq qalır.
2. JSON-un oxunması (deserializasiya)
Gəlin JSON sətrini oxuyub onu Java obyektinə çevirək. Bunun üçün bizə lazım olacaq:
- Məlumat sinfi (məsələn, Person)
- Jackson-dan ObjectMapper sinfi
Jackson-un qoşulması
Əgər Maven istifadə edirsinizsə, pom.xml-ə əlavə edin:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.0</version>
</dependency>
Gradle üçün — oxşar şəkildə:
implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.0'
Sinif nümunəsi
public class Person {
public String name;
public int age;
}
Qeyd: Sadəlik üçün sahələr publik edilib. Daha sonra özəl sahələr və getter/setter-lərlə işi müzakirə edəcəyik.
JSON nümunəsi
{
"name": "Alice",
"age": 30
}
Deserializasiya: JSON-u obyektə çeviririk
import com.fasterxml.jackson.databind.ObjectMapper;
public class Main {
public static void main(String[] args) throws Exception {
String json = "{\"name\": \"Alice\", \"age\": 30}";
ObjectMapper mapper = new ObjectMapper();
Person person = mapper.readValue(json, Person.class);
System.out.println(person.name); // Alice
System.out.println(person.age); // 30
}
}
Burada Jackson JSON sətrini parse edir, sinifin sahələri və ya getter-ləri ilə adı üst-üstə düşən sahələri tapır və readValue vasitəsilə obyektə uyğun dəyərləri doldurur.
Obyektlər siyahısının deserializasiyası
Tutaq ki, bizdə massiv var:
[
{ "name": "Bob", "age": 22 },
{ "name": "Eve", "age": 27 }
]
Siyahıya deserializasiya edək:
import com.fasterxml.jackson.core.type.TypeReference;
// ...
String json = "[{\"name\": \"Bob\", \"age\": 22}, {\"name\": \"Eve\", \"age\": 27}]";
ObjectMapper mapper = new ObjectMapper();
List<Person> people = mapper.readValue(json, new TypeReference<List<Person>>() {});
for (Person p : people) {
System.out.println(p.name + " (" + p.age + ")");
}
Bəlkə sual yarandı: niyə sadəcə mapper.readValue(json, List.class) yaza bilmirik? Yadınızdadırsa, Java-da generiklər kompilyasiya zamanı silinir (type erasure). Buna görə də Jackson-a siyahının içində Person tipli obyektlər olduğunu anlatmaq üçün TypeReference lazımdır.
3. JSON yazma (seriyalaşdırma)
İndi isə əks əməliyyatı edək: Java obyektini JSON sətrinə çevirək.
Nümunə
ObjectMapper mapper = new ObjectMapper();
Person person = new Person();
person.name = "Charlie";
person.age = 40;
String json = mapper.writeValueAsString(person);
System.out.println(json);
// {"name":"Charlie","age":40}
Obyektlər siyahısının seriyalaşdırılması
ObjectMapper mapper = new ObjectMapper();
List<Person> people = new ArrayList<>();
people.add(new Person("Anna", 25));
people.add(new Person("Dmitry", 31));
String json = mapper.writeValueAsString(people);
System.out.println(json);
// [{"name":"Anna","age":25},{"name":"Dmitry","age":31}]
Fayla yazma
ObjectMapper mapper = new ObjectMapper();
Person person = new Person();
person.name = "Charlie";
person.age = 40;
mapper.writeValue(new File("person.json"), person);
// person.json faylı indi JSON obyektini saxlayır
Oxunaqlı (pretty) JSON
Defolt olaraq Jackson hər şeyi bir sətirdə yazır. Amma daha “insan yönümlü” yazma variantı da var — pretty printing. Bu o deməkdir ki, JSON sətri oxunaqlı şəkildə çıxarılır: boşluqlar, sətir sonları və səliqəli formatlama ilə.
Kompaktlıq naminə əksər hallarda bir sətirdə yazılan “adi” JSON-dan fərqli olaraq, “oxunaqlı” variant insanlar üçündür — loglarda, fayllarda və ya ekranda məlumat strukturunu asan görmək üçün.
ObjectMapper mapper = new ObjectMapper();
Person person = new Person();
person.name = "Charlie";
person.age = 40;
String prettyJson = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(person);
System.out.println(prettyJson);
/*
{
"name" : "Charlie",
"age" : 40
}
*/
4. Jackson annotasiyaları
Jackson seriyalaşdırma və deserializasiya prosesini idarə etməyə imkan verən çoxlu annotasiyaları dəstəkləyir. Ən faydalıları bunlardır:
@JsonProperty
JSON-da sahənin adını, sinifdəki sahənin adından fərqlidirsə, göstərməyə imkan verir.
import com.fasterxml.jackson.annotation.JsonProperty;
public class Person {
@JsonProperty("full_name")
public String name;
public int age;
}
{"full_name": "Olga", "age": 28}
Jackson anlayacaq ki, JSON-dakı full_name sahəsi obyektin name sahəsinə yazılmalıdır.
@JsonIgnore
Hansısa sahəni seriyalaşdırmaq və ya deserializasiya etmək istəmirsinizsə:
import com.fasterxml.jackson.annotation.JsonIgnore;
public class Person {
public String name;
@JsonIgnore
public int age;
}
JSON-da age sahəsi, obyektin özündə olsa belə, olmayacaq.
@JsonInclude
Hansə sahələrin JSON-a düşəcəyini idarə edir. Məsələn, yalnız boş olmayan sahələri seriyalaşdırmaq:
import com.fasterxml.jackson.annotation.JsonInclude;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Person {
public String name;
public Integer age;
}
Əgər age == null olsa, JSON-da "age" açarı olmayacaq.
@JsonFormat
Tarix və vaxtın seriyalaşdırılması üçün formatı təyin etməyə imkan verir.
import com.fasterxml.jackson.annotation.JsonFormat;
import java.util.Date;
public class Event {
public String title;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
public Date date;
}
Event event = new Event();
event.title = "Hackathon";
event.date = new Date();
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(event);
// {"title":"Hackathon","date":"2024-06-07 15:23:00"}
Nümunə: hamısı birlikdə
import com.fasterxml.jackson.annotation.*;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Person {
@JsonProperty("full_name")
private String name;
private int age;
@JsonIgnore
private String password;
// Özəl sahələr üçün getter və setter-lər mütləqdir!
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}
5. Təcrübə: annotasiyalar nəzərə alınmaqla seriyalaşdırma və deserializasiya
Gəlin tədris tətbiqinizi genişləndirək — indi bizdə özəl sahələri, qeydiyyat tarixi olan və JSON-a düşməməli parolu olan User sinfi var.
import com.fasterxml.jackson.annotation.*;
import java.util.Date;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class User {
@JsonProperty("login")
private String username;
private int age;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private Date registered;
@JsonIgnore
private String password;
// Getter və setter-lər mütləqdir!
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public Date getRegistered() { return registered; }
public void setRegistered(Date registered) { this.registered = registered; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}
Seriyalaşdırma
import com.fasterxml.jackson.databind.ObjectMapper;
import java.text.SimpleDateFormat;
ObjectMapper mapper = new ObjectMapper();
User user = new User();
user.setUsername("superuser");
user.setAge(42);
user.setRegistered(new SimpleDateFormat("yyyy-MM-dd").parse("2024-06-07"));
user.setPassword("qwerty123"); // JSON-da olmayacaq!
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(user);
System.out.println(json);
/*
{
"login" : "superuser",
"age" : 42,
"registered" : "2024-06-07"
}
*/
Deserializasiya
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper mapper = new ObjectMapper();
String json = "{ \"login\": \"superuser\", \"age\": 42, \"registered\": \"2024-06-07\" }";
User user = mapper.readValue(json, User.class);
System.out.println(user.getUsername()); // superuser
System.out.println(user.getAge()); // 42
System.out.println(user.getRegistered());// Fri Jun 07 00:00:00 ...
System.out.println(user.getPassword()); // null (və bu yaxşıdır!)
6. Jackson ilə işləyərkən tipik səhvlər
Səhv №1: Parametrlərsiz konstruktor yoxdur.
Jackson sinfin parametrlərsiz konstruktoru yoxdursa, həmin sinfin obyektini yarada bilməyəcək. Bu, çox vaxt yalnız arqumentli konstruktor elan olunan siniflərdə rast gəlinir.
Səhv №2: Getter/setter-siz özəl sahələr.
Bütün sahələri özəl etmisinizsə, amma getter və setter əlavə etməyi unutmusunuzsa, Jackson deserializasiya zamanı (defolt olaraq) onları doldura bilməyəcək.
Səhv №3: Sahə adlarının uyğun gəlməməsi.
JSON-dakı sahə adı Java-dakı sahə/getter adından fərqlənəndə Jackson uyğunluğu tapa bilməyəcək. @JsonProperty-dən istifadə edin.
Səhv №4: Yanlış tarix formatı.
Əgər JSON-dakı tarix formatı Java-nın gözlədiyi formatla üst-üstə düşməzsə, Jackson parse xətası qaytaracaq. Tənzimləmə üçün @JsonFormat-dan istifadə edin.
Səhv №5: @JsonIgnore annotasiyalı sahələri seriyalaşdırmağa cəhd.
Belə sahələr JSON-a düşməyəcək — bu, bug yox, xüsusiyyətdir.
Səhv №6: Kolleksiyaların tip göstərilmədən seriyalaşdırılması/deserializasiyası.
Əgər TypeReference istifadə etməsəniz, Jackson kolleksiya daxilindəki obyektlərin tipini başa düşməyəcək.
Səhv №7: Faylı oxuma/yazma zamanı istisna.
Fayllarla işləyərkən IOException-u emal etməyi unutmayın.
GO TO FULL VERSION