Jackson to popularna biblioteka służąca do serializacji/deserializacji obiektów Java do różnych formatów tekstowych. Główną funkcjonalnością do pracy z formatem JSON jest klasa ObjectMapper . A jego spadkobiercy ( XmlMapper , YAMLMapper ) pomogą pracować z innymi formatami. Dzięki dziedziczeniu praca ze wszystkimi formatami będzie odbywać się jednakowo, za pośrednictwem jednego interfejsu.

Pobierz pseudonimy słoików

Przed przestudiowaniem przykładów musisz pobrać słoiki Jacksona i połączyć je z projektem w IntellijIDEA. Przyjrzyjmy się, jak wyszukać potrzebne pliki na przykładzie jackson-databind :

  1. Przejdź do witryny repozytorium Maven .

  2. Wpisz „ jackson-databind ” w pasku wyszukiwania, otrzymasz wynik:

  3. Interesuje nas pierwszy wynik wyszukiwania, kliknij link.

  4. Czasami może być wymagana określona wersja biblioteki, aby zapewnić kompatybilność z innymi komponentami w projekcie. Najnowsza wersja będzie Ci odpowiadać (w momencie pisania wykładu jest to 2.13.2.2), kliknij link.

  5. Na stronie, która się otworzy, potrzebujesz linku „pakietu”:

  6. Pobierz plik jar z łącza .

Podobnie możesz znaleźć i pobrać pozostałe niezbędne słoiki:

Po pobraniu wszystkich niezbędnych plików podłącz je do projektu w IntellijIDEA:

  1. Otwórz ustawienia projektu (można to zrobić za pomocą kombinacji Ctrl+Alt+Shift+S ).

  2. Przejdź do Biblioteki .

  3. Naciśnij + , następnie Java, wybierz wszystkie pobrane pliki. Powinno to wyglądać tak:

  4. To kończy przygotowania, wypróbujemy ObjectMappera w akcji.

Serializacja do JSON

Najpierw serializuj jakiś obiekt do JSON:


import com.fasterxml.jackson.databind.ObjectMapper;
 
class Book {
	public String title;
	public String author;
	public int pages;
}
 
public class Solution {
	public static void main(String[] args) throws Exception {
    	Book book = new Book();
    	book.title = "Good Omens";
    	book.author = "Pratchett T., Gaiman N.";
    	book.pages = 383;
 
    	ObjectMapper mapper = new ObjectMapper();
    	String jsonBook = mapper.writeValueAsString(book);
    	System.out.println(jsonBook);
	}
}

Kiedy uruchomisz main , otrzymasz to wyjście:

{"title":"Zamieszkana wyspa","author":"Strugatsky A., Strugacki B.","pages":413}

ObjectMapper ma wiele zaawansowanych opcji. Użyjmy jednego z nich, aby łańcuch JSON był czytelny i sformatowany. Po utworzeniu obiektuObiektMapperwykonajmy polecenie:

mapper.enable(SerializationFeature.INDENT_OUTPUT);

Informacje w danych wyjściowych pozostały takie same, ale dodano wcięcia i podziały wierszy:

{
  „tytuł”: „Wyspa zamieszkana”,
  „autor”: „Strugatsky A., Strugacki B.”,
 „strony”: 413
}

Deserializacja z JSON

Teraz zróbmy coś przeciwnego: przekształcimy łańcuch znaków w obiekt. Aby móc ocenić działanie programu dodaj metodę toString do klasy Book :


@Override
public String toString() {
	return "Book{" +
        	"title='" + title + '\'' +
        	", author='" + author + '\'' +
        	", pages=" + pages +
        	'}';
}

I wykonaj to główne :


public static void main(String[] args) throws Exception {
	String jsonString = "{\"title\":\"Good Omens\",\"author\":\"Pratchett T., Gaiman N.\",\"pages\":383}";
	Book book = new ObjectMapper().readValue(jsonString, Book.class);
	System.out.println(book);
}

Wniosek:

Książka{title='Wyspa zamieszkana', autor='Strugatsky A., Strugacki B.', strony=413}

Metoda readValue jest przeciążona, ma wiele odmian, które akceptują plik, link, różne strumienie odczytu itp. Dla uproszczenia, w naszym przykładzie używamy wariantu, który akceptuje JSON jako napis.

Jak wspomniano powyżej, ObjectMapper ma wiele ustawień, spójrzmy na niektóre z nich.

Ignorowanie nieznanych właściwości

Rozważmy sytuację, w której łańcuch JSON ma właściwość, której nie ma w klasie Book :


public static void main(String[] args) throws Exception {
	String jsonString = """
        	{
          	"title" : "Good Omens",
          	"author" : "Pratchett T., Gaiman N.",
          	"pages" : 383,
          	"unknown property" : 42
        	}""";
	ObjectMapper mapper = new ObjectMapper();
	Book book = mapper.readValue(jsonString, Book.class);
	System.out.println(book);
}

Próbując wykonać ten kod, otrzymujemy UnrecognizedPropertyException . To zachowanie jest ustawione domyślnie, ale możemy je zmienić:


ObjectMapper mapper =
new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

Podczas tworzenia obiektuObiektMapperużyj metody configure , aby ustawić żądane ustawienie na false . Metoda configure modyfikuje obiekt, do którego została wywołana, i zwraca ten sam obiekt, więc można ją wywołać w inny sposób:


ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

Pod względem funkcjonalności ten wpis jest podobny do poprzedniego.

Jeśli teraz uruchomimy main , deserializacja powiedzie się, a nieznana właściwość zostanie zignorowana.

Wygodne adnotacje

Jackson zapewnia nam szereg adnotacji do wszechstronnego dostosowywania serializacji. Rzućmy okiem na niektóre z najbardziej przydatnych:

@JsonIgnore - umieszczony nad elementem, który ma być ignorowany podczas serializacji/deserializacji:


class Book {
	public String title;
	@JsonIgnore
	public String author;
	public int pages;
}

W rezultacie podczas serializacji polaautornie zostaną uwzględnione w wynikowym formacie JSON. Podczas deserializacji polaautorotrzyma wartość domyślną (null), nawet jeśli w JSON była inna wartość.

@JsonFormat - pozwala ustawić format serializowanych danych. Dodajmy jeszcze jedno pole Date do klasy Book :


class Book {
	public String title;
	public String author;
	public int pages;
	public Date createdDate = new Date();
}

Po serializacji otrzymujemy następujący JSON:

 {
  "title": "Zamieszkana wyspa",
  "autor": "Strugatsky A., Strugatsky B.",
  "pages": 413,
  "createdDate": 1649330880788
}

Jak widać, data jest serializowana jako liczba. Dodaj adnotację i ustaw format:


class Book {
	public String title;
	public String author;
	public int pages;
	@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
	public Date createdDate = new Date();
}

Teraz wynik serializacji:

{
  "title" : "Wyspa zamieszkana",
  "autor": "Strugatsky A., Strugatsky B.",
  "pages" : 413,
  "createdDate" : "2022-04-07"
}

@JsonProperty — umożliwia zmianę nazwy właściwości, do której zostanie serializowane pole. Możesz również oznaczyć metody tą adnotacją, a ich wartość zwrotna zostanie przekonwertowana na właściwość JSON podczas serializacji:


class Book {
	@JsonProperty("name")
	public String title;
	public String author;
	public int pages;
 
	@JsonProperty("quotedTitle")
	public String getQuotedTitle() {
    	    return "\"" + title + "\"";
	}
}

Wynik serializacji:

{
  „autor”: „A. Strugacki, B. Strugacki”,
  „strony”: 413,
  „nazwa”: „Wyspa zamieszkana”,
  „cytowany tytuł”: „Wyspa zamieszkana”
}

@JsonInclude - za pomocą tej adnotacji możesz określić w jakim przypadku pole powinno być serializowane. Możesz dodać zarówno pojedyncze pola, jak i całą klasę. Najpierw spróbujmy serializować obiekt z niezainicjowanymi polami:


public class Solution {
	public static void main(String[] args) throws Exception {
    		Book book = new Book();

    		ObjectMapper mapper = new ObjectMapper();
    		mapper.enable(SerializationFeature.INDENT_OUTPUT);
    		String jsonBook = mapper.writeValueAsString(book);
    		System.out.println(jsonBook);
	}
}

Wynik serializacji:

{
  „tytuł”: zero,
  „autor”: zero,
  „strony”: 0
}

A jeśli dodasz adnotację:


@JsonInclude(JsonInclude.Include.NON_NULL)
class Book {
	public String title;
	public String author;
	public int pages;
}

Następnie otrzymujemy wynik:

{
  "strony" : 0
}

Teraz pola, które są puste , nie są serializowane.

@JsonPropertyOrder - pozwala ustawić kolejność serializacji dla pól:


@JsonPropertyOrder({"author", "title", "pages"})
class Book {
	public String title;
	public String author;
	public int pages;
}

Wynik serializacji:

{
  „autor”: „Strugatsky A., Strugacki B.”,
  „tytuł”: „Wyspa zamieszkana”,
  „strony”: 413
}

Na razie możesz po prostu zapamiętać, jak korzystać z adnotacji, a na końcu tego modułu poznamy je bardziej szczegółowo i stworzymy własne adnotacje.

Serializacja i deserializacja w XML

Jeśli potrzebujesz serializacji do formatu XML, możemy użyć tych samych ustawień i adnotacji. Jedyną różnicą będzie implementacja obiektuodwzorowujący:


public static void main(String[] args) throws Exception {
	Book book = new Book();
	book.title = "Good Omens";
	book.author = "Pratchett T., Gaiman N.";
	book.pages = 383;
 
	ObjectMapper mapper = new XmlMapper();
	mapper.enable(SerializationFeature.INDENT_OUTPUT);
	String xmlBook = mapper.writeValueAsString(book);
	System.out.println(xmlBook);
}

Wniosek:

 <Book>
  <title>Zamieszkana wyspa</title>
  <author>Strugatsky A., Strugacki B.</author>
  <pages>413</pages>
</Book>

Deserializacja XML:


public static void main(String[] args) throws Exception {
   String xmlString = """
            <Book>
             <title>Good Omens</title>
             <author>Pratchett T., Gaiman N.</author>
             <pages>383</pages>
           </Book>""";
   ObjectMapper mapper = new XmlMapper();
   Book book = mapper.readValue(xmlString, Book.class);
   System.out.println(book);
}

Serializacja i deserializacja w YAML

Podobnie postępujemy z XML z YAML:


public static void main(String[] args) throws Exception {
	Book book = new Book();
	book.title = "Good Omens";
	book.author = "Pratchett T., Gaiman N.";
	book.pages = 383;
 
	ObjectMapper mapper = new YAMLMapper();
	mapper.enable(SerializationFeature.INDENT_OUTPUT);
	String yamlBook = mapper.writeValueAsString(book);
	System.out.println(yamlBook);
}

Wniosek:

---
tytuł: "Zamieszkana wyspa"
autor: "Strugatsky A., Strugacki B."
strony: 413

Deserializacja YAML:


public static void main(String[] args) throws Exception {
   String yamlString = """
           ---
           title: "Good Omens"
           author: "Pratchett T., Gaiman N."
           pages: 383""";
   ObjectMapper mapper = new YAMLMapper();
   Book book = mapper.readValue(yamlString, Book.class);
   System.out.println(book);
}