1. Adapterlərdən istifadə (@XmlJavaTypeAdapter)
JAXB həqiqətən avtomat sürətlər qutusuna bənzəyir: hər şey standart olduqda – əla işləyir, amma nəsə qeyri-adi bir şey çıxanda, artıq əl ilə müdaxilə tələb olunur. Təsəvvür edin, sinfinizə LocalDate və ya BigDecimal tipində sahə əlavə etmisiniz. JAXB çaşa bilər – sadəcə onları XML-ə və geri necə çevirməyi bilmir. Yaxud istəyirsiniz ki, tarix uzun sətir 2024-06-01T00:00:00 kimi deyil, tanış formatda 01.06.2024 görünsün. Bəlkə də sizdə elə bir obyekt var ki, onu elementdə deyil, atributda saxlamaq daha məntiqlidir, yaxud iç-içə obyektləri olan kolleksiya xüsusi təqdimat tələb edir.
Bu halların hamısı adapterlərlə həll olunur. Onların köməyi ilə JAXB-a mürəkkəb sahələri məhz necə seriyalaşdırmaq və deseriyalaşdırmaq, hansı formatı tətbiq etmək və hətta lazımsız məlumatları ötürmək lazım olduğunu göstərə bilərsiniz. Bu, “sürətlərə” əl ilə nəzarətdir və avtomatın öhdəsindən gəlmədiyi yerdə sizə çeviklik verir.
Adapter nədir?
Adapter – bu, JAXB-a belə deyən xüsusi bir sinifdir: “Əgər belə bir tiplə rastlaşsan, onu belə seriyalaşdır, belə də deseriyalaşdır”. Java-da adapter javax.xml.bind.annotation.adapters.XmlAdapter<ValueType, BoundType> abstrakt sinfini reallaşdırır, burada:
- ValueType — məlumatın XML-də necə təqdim olunacağı (adətən String, bəzən Integer, Long və ya hətta başqa bir obyekt).
- BoundType — sizin Java sinfinizdəki real tip (məsələn, LocalDate).
Nümunə: LocalDate tipli sahənin seriyalaşdırılması
import java.time.LocalDate;
import javax.xml.bind.annotation.*;
@XmlRootElement
public class Person {
private String name;
private LocalDate birthDate; // Problem məhz buradadır!
public Person() {} // JAXB ictimai (public) parametrsiz konstruktor tələb edir
public Person(String name, LocalDate birthDate) {
this.name = name;
this.birthDate = birthDate;
}
@XmlElement
public String getName() { return name; }
public void setName(String name) { this.name = name; }
@XmlElement
public LocalDate getBirthDate() { return birthDate; }
public void setBirthDate(LocalDate birthDate) { this.birthDate = birthDate; }
}
Belə obyekt seriyalaşdırılmağa cəhd edilərsə, JAXB istisna atacaq:
javax.xml.bind.JAXBException: class java.time.LocalDate nor any of its super class is known to this context.
Addım 1: Adapter yaradırıq
import javax.xml.bind.annotation.adapters.XmlAdapter;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
// LocalDate <-> String çevrilməsi üçün adapter
public class LocalDateAdapter extends XmlAdapter<String, LocalDate> {
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("dd.MM.yyyy");
@Override
public LocalDate unmarshal(String v) throws Exception {
return (v == null || v.isEmpty()) ? null : LocalDate.parse(v, FORMATTER);
}
@Override
public String marshal(LocalDate v) throws Exception {
return (v == null) ? null : v.format(FORMATTER);
}
}
- marshal — Java obyektini (LocalDate) XML üçün sətirə çevirir.
- unmarshal — XML-dən sətiri geri Java obyektinə çevirir.
Addım 2: Sahəni və ya getter-i annotasiya ilə işarələyirik
@XmlJavaTypeAdapter(LocalDateAdapter.class)
public LocalDate getBirthDate() { return birthDate; }
Və ya annotasiyanı birbaşa sahənin üzərinə qoya bilərsiniz:
@XmlJavaTypeAdapter(LocalDateAdapter.class)
private LocalDate birthDate;
Addım 3: Nəticəni yoxlayırıq
İndi seriyalaşdırma zamanı obyekt belə görünəcək:
<Person>
<name>Ivan</name>
<birthDate>01.06.2024</birthDate>
</Person>
Və əksinə — XML-dən oxuyarkən, "01.06.2024" sətiri LocalDate obyektinə çevriləcək.
2. Adapterin sahəyə, getter-ə və ya bütün sinfə tətbiqi
Adapteri müxtəlif cür tətbiq etmək olar.
Ayrı bir sahəyə və ya getter-ə: Ən çox rast gəlinən hal.
@XmlJavaTypeAdapter(LocalDateAdapter.class)
private LocalDate birthDate;
Bütün sinfə: Əgər istəyirsinizsə ki, JAXB hansısa tipi həmişə adapter vasitəsilə seriyalaşdırsın, elə həmin sinfi işarələmək olar:
@XmlJavaTypeAdapter(LocalDateAdapter.class)
public class LocalDate { ... }
Adətən bunu öz sinifləriniz üçün edirlər, standartlar üçün yox ( LocalDate sinfini modifikasiya etmək mümkün deyil).
Kolleksiyalara: Məsələn, List<LocalDate>-i tarixlər siyahısını sətirlər siyahısına çevirən adapter vasitəsilə seriyalaşdırmaq olar.
3. Element və atribut adlarının fərdiləşdirilməsi
Bəzən XML strukturuna tələblər sərt olur: məsələn, sifarişçi sahənin <birthDate> deyil, <birth_date> adlandırılmasını və ya doğum tarixinin element yox, atribut olmasını istəyir.
Element adını dəyişmək
@XmlElement(name = "birth_date")
public LocalDate getBirthDate() { return birthDate; }
XML-də indi belə olacaq:
<birth_date>01.06.2024</birth_date>
Atribut kimi seriyalaşdırma
@XmlAttribute(name = "birth_date")
public LocalDate getBirthDate() { return birthDate; }
XML-də:
<Person birth_date="01.06.2024">
<name>Ivan</name>
</Person>
Adapterlə birgə istifadə
@XmlAttribute(name = "birth_date")
@XmlJavaTypeAdapter(LocalDateAdapter.class)
public LocalDate getBirthDate() { return birthDate; }
4. Praktik hallər
Sahələrin ötürülməsi (@XmlTransient)
Bəzən hansısa sahənin ümumiyyətlə XML-ə düşməməsi lazımdır (məsələn, daxili identifikator, parol, müvəqqəti məlumatlar).
@XmlTransient
private String internalCode;
Belə sahə seriyalaşdırma və deseriyalaşdırma zamanı nəzərə alınmayacaq.
Rəqəmlərin formatlaşdırılması
Tutaq ki, sizdə məbləğ sahəsi var:
private BigDecimal balance;
JAXB BigDecimal-ı sizə lazım olan formatda (məsələn, vergüldən sonra iki rəqəm, vergüllə ayırma) seriyalaşdırmağı bacarmır. Adapter yazırıq:
import javax.xml.bind.annotation.adapters.XmlAdapter;
import java.math.BigDecimal;
public class BigDecimalAdapter extends XmlAdapter<String, BigDecimal> {
@Override
public BigDecimal unmarshal(String v) throws Exception {
return (v == null || v.isEmpty()) ? null : new BigDecimal(v.replace(",", "."));
}
@Override
public String marshal(BigDecimal v) throws Exception {
return (v == null) ? null : String.format("%.2f", v);
}
}
Və istifadə edirik:
@XmlJavaTypeAdapter(BigDecimalAdapter.class)
private BigDecimal balance;
Daxili strukturlar
Əgər sizdə iç-içə obyektlər varsa, məsələn:
public class Address {
private String city;
private String street;
// ...
}
JAXB daxili obyektləri elementlər kimi özü seriyalaşdırır. Amma sizə lazımdırsa ki, məsələn, city atribut, street isə element olsun, annotasiyalardan istifadə edin:
public class Address {
@XmlAttribute
private String city;
@XmlElement
private String street;
}
5. Nümunə: Adapter ilə seriyalaşdırmanın tam sazlanması
Tətbiqi inkişaf etdirək: indi bizdə doğum tarixi və balansı olan Person sinfi var.
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.math.BigDecimal;
import java.time.LocalDate;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Person {
@XmlElement
private String name;
@XmlAttribute(name = "birth_date")
@XmlJavaTypeAdapter(LocalDateAdapter.class)
private LocalDate birthDate;
@XmlElement
@XmlJavaTypeAdapter(BigDecimalAdapter.class)
private BigDecimal balance;
@XmlTransient
private String password;
public Person() {}
public Person(String name, LocalDate birthDate, BigDecimal balance, String password) {
this.name = name;
this.birthDate = birthDate;
this.balance = balance;
this.password = password;
}
// getter və setter-lər...
}
Nə əldə etdik:
- Ad <name> elementi kimi seriyalaşdırılır.
- Doğum tarixi <Person birth_date="01.06.2024"> atributu kimi seriyalaşdırılır.
- Balans <balance>1234.56</balance> elementi kimi seriyalaşdırılır.
- Parol ümumiyyətlə XML-ə düşmür.
XML faylı:
<Person birth_date="01.06.2024">
<name>Ivan</name>
<balance>1234.56</balance>
</Person>
6. Kolleksiyaların və daxili obyektlərin emalı
JAXB kolleksiyalarla düzgün annotasiya olunublarsa işləməyi bacarır. Məsələn, əgər şəxsin ünvan siyahısı varsa:
@XmlElementWrapper(name = "addresses")
@XmlElement(name = "address")
private List<Address> addresses;
XML-də bu, belə görünəcək:
<addresses>
<address city="Berlin">
<street>Alexanderplatz, 1</street>
</address>
<address city="Limassol">
<street>Anexartisias, 10</street>
</address>
</addresses>
Əgər kolleksiyadakı tip qeyri-standartdırsa (məsələn, List<LocalDate>), adapteri kolleksiya elementinə tətbiq etmək olar:
@XmlElementWrapper(name = "dates")
@XmlElement(name = "date")
@XmlJavaTypeAdapter(LocalDateAdapter.class)
private List<LocalDate> importantDates;
7. Nümunə: adapterlə seriyalaşdırma və deseriyalaşdırma
Seriyalaşdırma
Person person = new Person(
"Ivan",
LocalDate.of(1990, 6, 1),
new BigDecimal("1234.56"),
"secretPassword"
);
JAXBContext context = JAXBContext.newInstance(Person.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(person, System.out); // XML-i konsola çıxaracaq
Deseriyalaşdırma
String xml = """
<Person birth_date="01.06.1990">
<name>Ivan</name>
<balance>1234.56</balance>
</Person>
""";
JAXBContext context = JAXBContext.newInstance(Person.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
Person person = (Person) unmarshaller.unmarshal(new StringReader(xml));
System.out.println(person.getName() + " " + person.getBirthDate() + " " + person.getBalance());
8. Seriyalaşdırma və adapterlərin sazlanması zamanı tipik xətalar
Xəta №1: Parametrsiz public konstruktorun olmaması. Əgər o yoxdursa, JAXB deseriyalaşdırma zamanı obyekti yarada bilməyəcək və istisna atacaq.
Xəta №2: Adapterin yanlış tətbiqi. @XmlJavaTypeAdapter-i səhv sahəyə qoysanız və ya getter/setter-i unutsanız, JAXB lazımi tipi necə seriyalaşdıracağını bilməyəcək.
Xəta №3: Deseriyalaşdırma zamanı formatın uyğun gəlməməsi. Əgər XML-də tarix adapterinizin dəstəkləmədiyi formatdadırsa (məsələn, "2024-06-01" əvəzinə "01.06.2024"), unmarshal metodu istisna atacaq.
Xəta №4: Adapter olmadan JAXB-in dəstəkləmədiyi tipi seriyalaşdırmağa cəhd. Tipik nümunələr — LocalDate, BigDecimal, Map, istifadəçi tərəfindən yaradılan mürəkkəb tiplər.
Xəta №5: Annotasiyalar olmadan daxili kolleksiyaların gözardı edilməsi. @XmlElementWrapper olmadan kolleksiya gözlənildiyi kimi seriyalaşdırılmaya bilər və ya JAXB ümumiyyətlə XML-i düzgün oxuya bilməz.
Xəta №6: Adapterin kolleksiyaya, elementə yox, birbaşa siyahıya tətbiqi. Siyahının elementlərini adapter vasitəsilə seriyalaşdırmaq istəyirsinizsə, annotasiyanı elementə qoyun, siyahının özünə deyil (məsələn, @XmlJavaTypeAdapter element sahəsinin üzərində və ya yuxarıdakı nümunələrdə olduğu kimi element tipini göstərə-göstərə siyahı sahəsinin üzərində).
GO TO FULL VERSION