CodeGym /Kurslar /JAVA 25 SELF /XML seriyalaşdırılmasını sazlama: xüsusi adapterlər

XML seriyalaşdırılmasını sazlama: xüsusi adapterlər

JAVA 25 SELF
Səviyyə, Dərs
Mövcuddur

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ə).

1
Tapşırıq
JAVA 25 SELF, səviyyə, dərs
Bağlanıb
Hadisə Tarixinin Atribut kimi yazılması 📅
Hadisə Tarixinin Atribut kimi yazılması 📅
1
Tapşırıq
JAVA 25 SELF, səviyyə, dərs
Bağlanıb
Təqvimdə Bayram Tarixləri Siyahısının İdarə Edilməsi 🎉
Təqvimdə Bayram Tarixləri Siyahısının İdarə Edilməsi 🎉
1
Sorğu/viktorina
, səviyyə, dərs
Əlçatan deyil
XML seriyalaşdırılması
XML seriyalaşdırılması
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION