===== Zapis i odczyt dokumentu ===== Zakładamy, że utworzony programowo dokument został zapisany w postaci pliku HTML. Co jednak, gdybyśmy chcieli mieć możliwość zapisu i późniejszego odczytu zawartości dokumentu? Jednym z wygodnych rozwiązań jest zastosowanie JAXB (Java Architecture for XML Binding), biblioteki pozwalającej na zapis złożonej struktury obiektów w pamięci w formacie XML (//ang. marshal//) i późniejsze odtworzenie struktury obiektów z dokumentu XML (//ang. unmarshal//​). ​ Aby zapis i odczyt był mozliwy musi być spełnionych kilka warunków: *musimy **oznaczyć** elementy, które chcemy zapisać (można też wymusić użycie specyficznych tagów lub atrybutów XML). Czyli raczej //​oznaczamy//​ niż //​programujemy//,​ ale oszczędzamy bardzo dużo czasu! *ponieważ przy odczycie tworzone są nowe puste obiekty na podstawie tagów, może być wymagane dodanie standardowych konstruktorów (tam gdzie są obecne konstruktory z parametrami). Dla przypomnienia ''<​to:​jest:​tag att="a to jest atrybut">''​ a tag zamykający wygląda tak ''''​. ==== Adnotacje ==== Biblioteka JAXB używa specyficznego mechanizmu języka - adnotacji (//ang. annotations//​) [który chyba nie będzie omawiany na wykładzie ???] Adnotacje mają postać ''​@Identifier''​ lub ''​@Identifier(key=value[,​otherkey=othervalue])''​ i są umieszczane w kodzie programu, zazwyczaj przed definicją klasy, deklaracją atrybutu lub definicją metody. Na przykład: @Override public String toString(){ return "";​ } ​ Adnotacje nie mają bezpośredniego wpływu na wykonanie kodu, ale mogą być wykorzystywane przez zewnętrzne narzędzia przetwarzające kod (zarówno źródłowy,​ jak i skompilowany): ​ * kompilator * biblioteki, które na podstawie adnotacji dowiadują się, w jaki sposób przetwarzać obiekty, na przykład: * wypełniać ich atrybuty danymi zapisanymi w zewnętrznych źródłach (dokumentach,​ bazach danych) * zapisywać obiekty w specyficznych formatach * wybierać specyficzne elementy, np. funkcje służące do testowania ''​@org.junit.Test''​ * wiązać wywołania metod i ich parametry z interfejsem sieciowym Formalnie, adnotacje deklaruje się jak interfejsy i umieszcza w pakietach :!: ** Dodając adnotacje zadbaj, aby importować elementy z pakietu ''​javax.xml.bind.annotation''​ ** ==== Metody do zapisu i odczytu ==== Dodaj w klasie ''​Document''​ metody do zapisu i odczytu public void write(String fileName){ try { JAXBContext jc = JAXBContext.newInstance(Document.class);​ Marshaller m = jc.createMarshaller();​ m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,​ Boolean.TRUE);​ FileWriter writer= new FileWriter(fileName);;​ m.marshal(this,​ writer); } catch (JAXBException ex) { ex.printStackTrace();​ } catch (IOException ex) { ex.printStackTrace();​ } } public static Document read(String fileName){ try { JAXBContext jc = JAXBContext.newInstance(Document.class);​ Unmarshaller m = jc.createUnmarshaller();​ FileReader reader = new FileReader(fileName);​ return (Document) m.unmarshal(reader);​ } catch (JAXBException ex) { ex.printStackTrace();​ } catch (FileNotFoundException ex) { ex.printStackTrace();​ } return null; } ​ Dodaj także w funkcji ''​main()''​ kod, który zapisuje (i odczytuje) dokument cv.write("​cv.xml"​);​ //Document cv2 = Document.read("​cv.xml"​);​ //​cv2.writeHTML(System.out);​ ​ === Dla użytkowników Java 9 (zazwyczaj na laptopach) === Java SE 9 nie widzi części modułów Java EE, w tym JAXB. 1. W IntelliJ w dialogu //Project Structure// - ustaw **Project language Level** : 8 - lambdas, type annotations ​ 2. Zalecanym obejściem (w [[https://​blog.codefx.org/​java/​java-9-migration-guide/​]]) jest dodanie opcji uruchamiania programu <​code>​ --add-modules java.se.ee ​ W IntelliJ nalezy je wpisac w oknie //Run -> Edit Configurations -> pole **VM options**// ==== Dodajemy adnotacje ==== W następnych etapach będziemy modyfikowali kod (dodawali adnotacje) i sprawdzali, co zmienia się w pliku ''​cv.xml''​ Najczęściej używane będą następujące adnotacje *''​@XmlRootElement''​ - oznaczenie klasy będącej korzeniem drzewa XML *''​@XmlElement''​ - oznaczenie, że atrybut ma zostać zapisany jako element XML (jako nazwa znacznika XML zostanie użyta nazwa atrybutu) *''​@XmlElement(name="​xyz"​)''​ - podana jest nazwa znacznika ''​xyz'' ​ *''​XmlAttribute''​ atrybut (pole klasy) zostanie zapisany jako atrybut XML ==== Oznacz Document jako XMLRootElement ==== @XmlRootElement public class Document { ... } ​ Zobacz, co zostało zapisane do pliku ''​cv.xml''​ ==== Oznacz atrybuty klasy Document ​ ==== @XmlElement String title; @XmlElement List<​Section>​ sections = new ArrayList<>​();​ @XmlElement Photo photo; ​ Wykonaj program i spróbuj zidentyfikować i naprawić błąd... ==== Photo ==== Pole ''​url''​ odwzorujemy w atrybut @XmlAttribute String url; ​ ==== Section ​ ==== Tytuł jest zapewne krótki - odwzorujemy go w atrybut, natomiast listę akapitów (''​paragraphs''​) w XmlElement ==== Poprawiamy nazwy znaczników XML ==== Zastąp ​ w odpowiednim miejscu ''​@XmlElement''​ przez ''​@XmlElement(name="​section"​)''​ i ''​@XmlElement(name="​paragraph"​)''​. ==== Nie widać listy w cv.xml? ​ ==== 1. Uzupełnijmy więc znaczniki w ''​ParagraphWithList'',​ UnorderedList oraz ''​ListItem''​ Prawdopodobnie dalej lista w ''​cv.xml''​ nie będzie widoczna... 2. Poinformuj, JAXB o klasach potomnych ''​Paragraph''​ @XmlSeeAlso({ParagraphWithList.class}) public class Paragraph { ​ ... i usuń ewentualne błędy. 3. Możesz przyjrzeć się znacznikom i poprawić nazwy według uznania