Laboratorium 4 - piszemy CV

Zaimplementujemy kilka klas, które pozwolą na utworzenie strukturalnego dokumentu - i wypełnimy przykładowymi danymi tak, aby powstało CV.

Dokument będzie miał:

  • tytuł
  • zdjęcie
  • składał się z kilku sekcji
  • sekcje będą zawierały akapity (paragraph)
  • Szczególnym typem akapitu będzie akapit zawierający listę
  • całość ma być zapisywana w formacie HTML (który zapewne jest dobrze znany)

Analiza struktury

Tak wygląda dokument

A tak diagram klas

Zaimplementuj kolejne klasy

Photo

Klasa Photo jest raczej prosta

public class Photo {
    Photo(String url){
        this.url =url;
    }
    String url;
    void writeHTML(PrintStream out){
        out.printf("<img src=\"%s\" alt=\"Smiley face\" height=\"42\" width=\"42\"/>\n",url)
    }
}

Document

Klasa powinna mieć następujące atrybuty:

    String title;
    Photo photo;
    List<Section> sections = new ArrayList<>();

oraz metody:

    Document setTitle(String title){
        this.title = title;
        return this;
    }
 
    Document setPhoto(String photoUrl){
        // ???
        return this;
    }
 
    Section addSection(String sectionTitle){
        // utwórz sekcję o danym tytule i dodaj do sections
        return ???;
    }
    Document addSection(Section s){
        return this;
    }
 
 
    void writeHTML(PrintStream out){
    // zapisz niezbędne znaczniki HTML
    // dodaj tytuł i obrazek
    // dla każdej sekcji wywołaj section.writeHTML(out)
    }

Możesz dodać konstruktor ustalający tytuł dokumentu.

Section

Sekcja powinna mieć następujące atrybuty:

    String title;
    List<Paragraph> paragraps = new ArrayList<>() ;

oraz metody

Section setTitle(String title){}
Section addParagraph(String paragraphText){}
Section addParagraph(Paragraph p){}
void writeHTML(PrintStream out){}

Możesz dodać konstruktor ustalający tytuł sekcji.

Paragraph

Atrybut content to treść akapitu.

  • Metoda setContent() zmienia treść
  • Metoda writeHTML() powinna umieszczać treść pomiędzy znacznikam <p>…</p>
  • Możesz też dodać konstruktor

Inne klasy...

Dodaj na podstawie diagramu

  • ParagraphWithList ma dodatkowy atrybut - listę typu UnorederedList; można ją wypisać wewnątrz znaczników <p>…</p> lub po znaczniku zamykającym.
  • UnorederedList przechowuje na liście elementy ListItem
  • ListItem zawiera wyłącznie tekst

Klasa UnorederedList została wprowadzona, ponieważ będzie odpowiedzialna za wypisywanie

<ul>
...
...
</ul>

Podobnie ListItem ma wypisywać tekst pomiędzy znacznikami <li>…</li>

Zbuduj dokument z CV

Spróbuj dobrać funkcje tak, aby budowało się w miarę wygodnie. Na przykład tak:

        Document cv = new Document("Jana Kowalski - CV");
        cv.setPhoto("https://upload.wikimedia.org/wikipedia/commons/thumb/7/71/Calico_tabby_cat_-_Savannah.jpg/1200px-Calico_tabby_cat_-_Savannah.jpg");
        cv.addSection("Wykształcenie")
                .addParagraph("2000-2005 Przedszkole im. Królewny Snieżki w ...")
                .addParagraph("2006-2012 SP7 im Ronalda Regana w ...")
                .addParagraph(
                        new ParagraphWithList().setContent("Kursy")
                                .addListItem("Języka Angielskiego")
                                .addListItem("Języka Hiszpańskiego")
                                .addListItem("Szydełkowania")
                );
        cv.addSection("Umiejętności")
                .addParagraph(
                        new ParagraphWithList().setContent("Znane technologie")
                                .addListItem("C")
                                .addListItem("C++")
                                .addListItem("Java")
                );

Możesz wygenerować swoje CV lub wyimaginowanej osoby, ale zadbaj, aby znalazło się w nim wystarczająco dużo informacji.

Wygeneruj kod HTML strony

Powinno to być jedno wywołanie, jak (wypisujące na konsoli)

cv.writeHTML(System.out);

lub (wpisujące do pliku)

cv.writeHTML(new PrintStream("cv.html","ISO-8859-2"));
// lub
cv.writeHTML(new PrintStream("cv.html","UTF-8"));

Wygeneruj poprawny składniowo dokumenty HTML. Możesz zastosować zdefiniowane w nagłówku style CSS, aby zapewnić ładne formatowanie.

Napisz testy

Głównym celem klas jest generacja kodu w HTML. Testy powinny więc sprawdzać głównie poprawność generacji: obecność znaczników i danych w wygenerowanym kodzie…

Co może być sprawdzone dla wyjścia typu <img src=“jan-kowalski.png” alt=“Smiley face” height=“42” width=“42”/>?

Przykład implementacji

public class PhotoTest {
    @org.junit.Test
    public void writeHTML() throws Exception {
        String imageUrl = "jan-kowalski.png";
        // Utwórz strumień zapisujący w pamięci
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        PrintStream ps = new PrintStream(os);
        // Utwórz obiekt i zapisz do strumienia
        new Photo(imageUrl).writeHTML(ps);
        String result = null;
        // Pobierz jako String
        try {
            result = os.toString("ISO-8859-2");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
 
        //System.out.println(result);
 
        // Sprawdź, czy result zawiera wybrane elementy
        assertTrue(result.contains("<img"));
        assertTrue(result.contains("/>"));
        assertTrue(result.contains("src="));
        assertTrue(result.contains(imageUrl));
 
    }
 
}

Napisz testy sprawdzające poprawność generacji kodu dla trzech wybranych klas.

Zapis i odczyt dokumentu w formacie JSON

W poprzedniej wersji lab4_old częścią zadania był zapis i odczyt danych dokumentu w formacie XML. Obsługa XML za pomocą biblioteki JAXB została wycofana z dystrybucji Java SE. Jest częścią Java EE (Enterprise Edition), gdzie wykorzystywana jest do obsługi protokołu SOAP służącego do zdalnego wywołania procedur. Zamiast XML użyjemy więc formatu JSON.

Dodaj bibliotekę Gson do projektu

Spośród dostępnych bibliotek do obsługi formatu JSON https://www.baeldung.com/java-json użyjemy Gson

  • Wybierz opcję Project Structure → Libraries, a następnie przycisk + (New project library)
  • Wybierz From maven i podaj nazwę biblioteki google.code.gson. Następnie aktywuj lupę (wyszukiwanie) i wybierz wersję, np. 2.10.1

Generalnie, InteliJ ma (może miewał?) czasem problemy ze znajdywaniem bibliotek. Warto sprawdzić wersje w Maven Repository. Z nieznanych powodów InteliJ nie jest w stanie ich znaleźć, ale jest w stanie pobrać i zainstalować.

Serializacja

Serializacja to proces zamiany obiektów na format służący do przechowywania lub transferu danych. Często też w języku angielskim jets określana terminem marshaling, zwłaszcza w kontekście serializacji obiektów w celu przesłania ich przez sieć, patrz Wikipedia

Dodaj kod w klasie Dokument

String toJson(){
        Gson gson = new GsonBuilder().setPrettyPrinting().create();
        return gson.toJson(this);
    }

Wypisz zwrócony tekst. Efekt powinien być podobny do poniższego:

{
  "title": "Jana Kowalski - CV",
  "photo": {
    "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/7/71/Calico_tabby_cat_-_Savannah.jpg/1200px-Calico_tabby_cat_-_Savannah.jpg"
  },
  "sections": [
    {
      "title": "Wykształcenie",
      "paragraphs": [
        {
          "content": "2000-2005 Przedszkole im. Królewny Snieżki w ..."
        },
        {
          "content": "2006-2012 SP7 im Ronalda Regana w ..."
        },
        {
          "list": {
            "items": [
              {
                "content": "Języka Angielskiego"
              },
              {
                "content": "Języka Hiszpańskiego"
              },
              {
                "content": "Szydełkowania"
              }
            ]
          },
          "content": "Kursy"
        }
      ]
    },
    {
      "title": "Umiejętności",
      "paragraphs": [
        {
          "list": {
            "items": [
              {
                "content": "C"
              },
              {
                "content": "C++"
              },
              {
                "content": "Java"
              }
            ]
          },
          "content": "Znane technologie"
        }
      ]
    }
  ]
}

Deserializacja

Dodaj w klasie Dokument statyczną funkcję zamieniająca tekst na obiekt dokument

Document fromJson(String jsonString){
        Gson gson = new GsonBuilder().create();
        return gson.fromJson(jsonString, Document.class);
    }

Wywołaj tę funkcje i przeanalizuj zawartość odtworzonego dokumentu. Na przykład zapisz w formacie HTML lub JSON.

Wniosek: obiekty klasy ParagraphWithList nie zostały poprawnie wczytane.

Obsługa hierarchii klas

Hierarchie klas są na ogół problemem podczas deserializacji. Jeżeli w klasie Section lista akapitów jest zdefiniowana jako List<Paragraph> paragraphs = new ArrayList<>(); biblioteka spodziewa się obiektów klasy bazowej, a nie potomnej. Tworzy więc obiekty klasy bazowej i ignoruje listę będącą atrybutem ParagraphWithList

Jednym z rozwiązań jest jawne przekazanie informacji o typie.

  • Dodaj do projektu bibliotekę danilopanini.gson.extras. Dla gson 2.10.1 gson-extras w wersji 0.2.1 zadziałało poprawnie.
  • Podczas serializacji przekaż informacje o istniejącej hierarchii klas
    String toJson(){
        RuntimeTypeAdapterFactory<Paragraph> adapter =
                RuntimeTypeAdapterFactory
                        .of(Paragraph.class)
                        .registerSubtype(Paragraph.class)
                        .registerSubtype(ParagraphWithList.class);
        Gson gson = new GsonBuilder().registerTypeAdapterFactory(adapter).setPrettyPrinting().create();
        return gson.toJson(this);

W tym przypadku wynik powinien być następujący:

{
  "title": "Jana Kowalski - CV",
  "photo": {
    "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/7/71/Calico_tabby_cat_-_Savannah.jpg/1200px-Calico_tabby_cat_-_Savannah.jpg"
  },
  "sections": [
    {
      "title": "Wykształcenie",
      "paragraphs": [
        {
          "type": "Paragraph",
          "content": "2000-2005 Przedszkole im. Królewny Snieżki w ..."
        },
        {
          "type": "Paragraph",
          "content": "2006-2012 SP7 im Ronalda Regana w ..."
        },
        {
          "type": "ParagraphWithList",
          "list": {
            "items": [
              {
                "content": "Języka Angielskiego"
              },
              {
                "content": "Języka Hiszpańskiego"
              },
              {
                "content": "Szydełkowania"
              }
            ]
          },
          "content": "Kursy"
        }
      ]
    },
    {
      "title": "Umiejętności",
      "paragraphs": [
        {
          "type": "ParagraphWithList",
          "list": {
            "items": [
              {
                "content": "C"
              },
              {
                "content": "C++"
              },
              {
                "content": "Java"
              }
            ]
          },
          "content": "Znane technologie"
        }
      ]
    }
  ]
}

Ostateczna wersja

  • Analogicznie skonfiguruj Gson w przypadku deserializacji
  • Przetestuj sekwencję operacji:
    1. Serializacja do formatu JSON
    2. Deserializacja
    3. Powtórna serializacja do formatu JSON
  • i sprawdź, czy wyniki (1) i (3) są identyczne. Możesz to zrealizować jako test jednostkowy - ale powinieneś też sprawdzić, czy tekst obejmuje pełną zwartość ParagraphWithList.

Pliki do przesłania

  • Kod Java
  • Plik HTML zawierający wydrukowane CV
  • Plik w formacie JSON
pz1/lab4.txt · Last modified: 2023/10/28 04:15 by pszwed
CC Attribution-Share Alike 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0