# Serialització amb java
### Què és la serialització?
La serialització (marshalling) és el procés amb el qual codifiquem un objecte per tal de transmetre'l per una xarxa a qualsevol altre dispositiu.
Cal considerar algunes coses:
* L'objecte a transmetre (serialitzat) ha d'estar prèviament persistit d'alguna forma, ja sigui en un fitxer, als buffers de memòria, etc.
* Els objectes tenen estats. Quan algun dels atributs que forma part de l'objecte canvia de valor, diem que canvia l'estat de l'objecte. La serialització codifica l'estat d'un objecte per poder-lo transmetre.
* La transmissió pot fer-se com una sèrie de bytes o bé en seqüències inteligibles per l'èsser humà com XML o JSON.
Així, un cop l'objecte serialitzat arriba al dipositiu de destí cal fer el procés contrari, és a dir, deserialitzar-lo. I un cop fet es torna a persistir en un altre mitjà com pot ser un fitxer, base de dades, etc.
### Com s'implementa?
Java utilitza una **interfície** anomenada **serializable**.
Per entendre com funciona, imaginem que tenim una classe anomenada Productes, definida com un POJO (Plain Old Java Object). Un POJO és una classe simple, on fonamentalment hi ha els constructors, getters i setters. Dins un POJO no veurem mètodes amb lògica sobre els atributs de la classe o d'altres classes, tipus fer un càlcul o determinar un valor en funció de condicions. (https://www.arquitecturajava.com/que-es-un-pojo-en-java/).
Dit això, la classe Productes quedaria com segueix:
```
import java.io.Serializable;
public class Productes implements Serializable {
private static final long serialVersionUID = 1L;
private int idProd;
private String descProd;
private int amountProd;
private double unitPrice;
public Productes() {}
public Productes(int idProd, String descProd, int amountProd,
double unitPrice) {
super();
this.idProd = idProd;
this.descProd = descProd;
this.amountProd = amountProd;
this.unitPrice = unitPrice;
}
public int getIdProd() {
return idProd;
}
public void setIdProd(int idProd) {
this.idProd = idProd;
}
public String getDescProd() {
return descProd;
}
public void setDescProd(String descProd) {
this.descProd = descProd;
}
public int getAmountProd() {
return amountProd;
}
public void setAmountProd(int amountProd) {
this.amountProd = amountProd;
}
public double getUnitPrice() {
return unitPrice;
}
public void setUnitPrice(double unitPrice) {
this.unitPrice = unitPrice;
}
@Override
public String toString() {
return "Productes [idProd=" + idProd + ",
descProd=" + descProd + ",
amountProd=" + amountProd + ",
unitPrice=" + unitPrice + "]";
}
}
```
Fixem-nos que hem introduït d'una banda l'import de java.io.serializable, a més d'una constant UID, la qual ens permet assegurar que tots els dispositius tenen la mateixa versió d'una classe. Això és important per totes aquelles màquines que han de deserialitzar un objecte.
#### Procés de serialització
Per fer el procés de serialització necessitarem:
* Crear un objecte *ObjectOutputStream* (a l'exemple l'anomenem serializador, però pot dur qualsevol nom).
* Crear un OutputStrem, el qual embolicarem dins l'ObjectOutputStream. Com es tracta de serialitzar a un fitxer, farem servir *FileOutputStream*.
* Ja per últim farem el *writeObject(object o)* per serialitzar l'objecte i passar-lo a l'OutputStream. En el cas de l'exemple, l'objecte a serialitzar és un array de productes anomenat prods, el qual passem dins de writeObject.
Traslladat tot això a codi, ens queda com:
```
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class StoreSerialProds {
public static void main(String[] args) {
Productes[] prods = new Productes[4];
prods[0] = new Productes(1,"Coca Cola",200,0.65);
prods[1] = new Productes(2,"White Label",20,14.50);
prods[2] = new Productes(3,"Gel",100,1.50);
prods[3] = new Productes(4,"Snacks",300,1.20);
ObjectOutputStream serializador = null;
try {
// instanciem l'object output stream
serializador = new ObjectOutputStream(
new FileOutputStream("ProdsSer.dat"));
// fem el writeObject
serializador.writeObject(prods);
// tanquem object output stream : IMPORTANT!!
serializador.close();
} catch (IOException ioe) {
System.out.print(ioe.getMessage());
}
}
}
```
Observeu que hem creat un array de productes i l'hem omplert de forma manual, si bé es podria fer demanant a l'usuari per pantalla que ens doni les dades necessàries per omplir les ocurrències d'aquest.
#### Procés de Desserialització
El procés contrari és la desserialització, la qual es donarà en les màquines/dispositius que rebin l'objecte serialitzat.
Com ja hem comentat, aquestes màquines hauran de disposar de la mateixa versió de la classe, garantida mitjançant el serial version UID.
Per fer el procés de desserialització necessitarem:
* Crear un objecte *ObjectInputStream* (a l'exemple l'anomenem deserializador, però pot dur qualsevol nom).
* Crear un InputStream, el qual embolicarem dins l'ObjectInputStream. Com es tracta de serialitzar a un fitxer, farem servir *FileInputStream*.
* Ja per últim farem el *readObject(object o)* per reconstruir l'objecte a partir de la seqüència de bits proveïda per l'ObjectInputStream. En el cas de l'exemple, la reconstitució es fa fent un **cast** al tipus d'objecte, és a dir, a (Prods[]).
Atès que és un array de productes, si volem veure quines són les seves ocurrències farem un una iteració. De forma simple el que farem es mostrar el resultat per pantalla, però es pot fer qualsevol altre tractament.
Traslladat tot això a codi, ens queda com:
```
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
public class DeSerialProds {
public static void main(String[] args) {
ObjectInputStream deserializador = null;
// Object listaProductes = null;
try {
deserializador = new ObjectInputStream(
new FileInputStream("ProdsSer.dat"));
Productes[] listaProductes = (Productes[])deserializador.readObject();
for(Productes p:listaProductes) {
System.out.println(p);
}
deserializador.close();
} catch (FileNotFoundException fnfe ) {
fnfe.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
```
El resultat d'executar aquest codi és:
```
Productes [idProd=1, descProd=Coca Cola, amountProd=200, unitPrice=0.65]
Productes [idProd=2, descProd=White Label, amountProd=20, unitPrice=14.5]
Productes [idProd=3, descProd=Gel, amountProd=100, unitPrice=1.5]
Productes [idProd=4, descProd=Snacks, amountProd=300, unitPrice=1.2]
```
### Serialització a JSON amb Jackson
Una altra possibilitat és serialitzar objectes fent servir JSON en comptes de bytes i streams de bytes.
Per fer-ho cal crear un nou projecte on incorporarem el gestor de dependències maven. D'aquesta forma ens estalviarem haver de baixar i instal·lar res. Mostrarem tot seguit com fer-ho amb visual Studio Code. Assumirem que teniu instal·lat l'**Extension pack for java**, en cas contrari instal·leu-lo.
El primer pas es fer CTRL + SHIFT + P i escriure que volem un nou projecte maven:

A continuació li diem que no volem cap arquetip de projecte, ja que no ens farà falta:

I anem omplint el que ens va demanant d'aquesta forma (groupid, artifactid, ):


Ens demanarà desar-ho en una carpeta del local. Trieu la que considereu (en aquest exemple triem Documents):

Un cop l'obriu haurieu de localitzar la carpeta:

Si la desplegueu, veureu que hi ha un arxiu anomenat pom.xml:

L'obrim i l'editem per tal d'afegir aquí una dependència. Ens el trobarem així:

i l'haurem de deixar així:
```
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.projecte1</groupId>
<artifactId>serialjackson</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.2</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
</project>
```
Tingueu present que aquesta no és la darrera versió d'aquesta dependència. Des de [Maven Central - Jackson](https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind) podreu obtenir la darrera.
Desem el contingut i al fer-ho a la cantonada inferior dreta ens apareixerà aquest missatge:

Diem que yes per tal que es considerin els canvis deguts a la dependència afegida.
Ara desplegarem la carpeta src de fonts:

Afegirem aquí la classe Productes però li treurem l'ús de la interfície serializable:
```
public class Productes {
private int idProd;
private String descProd;
private int amountProd;
private double unitPrice;
public Productes() {}
public Productes(int idProd, String descProd, int amountProd, double unitPrice) {
super();
this.idProd = idProd;
this.descProd = descProd;
this.amountProd = amountProd;
this.unitPrice = unitPrice;
}
public int getIdProd() {
return idProd;
}
public void setIdProd(int idProd) {
this.idProd = idProd;
}
public String getDescProd() {
return descProd;
}
public void setDescProd(String descProd) {
this.descProd = descProd;
}
public int getAmountProd() {
return amountProd;
}
public void setAmountProd(int amountProd) {
this.amountProd = amountProd;
}
public double getUnitPrice() {
return unitPrice;
}
public void setUnitPrice(double unitPrice) {
this.unitPrice = unitPrice;
}
@Override
public String toString() {
return "Productes [idProd=" + idProd + ", descProd=" + descProd + ", amountProd=" + amountProd + ", unitPrice="
+ unitPrice + "]";
}
}
```
#### Procés de serialització (Objecte --> JSON)
Un cop ja tenim la classe que volem serialitzar, anirem al main i afegirem el següent codi:
```
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Main {
public static void main(String[] args) throws JsonProcessingException{
Productes prod = new Productes(1,"Pomes",10,0.60);
String jsonString = new ObjectMapper().writeValueAsString(prod);
System.out.println(jsonString);
}
}
```
L'output que produeix l'execució seria el següent:
```
{"idProd":1,"descProd":"Pomes","amountProd":10,"unitPrice":0.6}
```
Si volem ara que aparegui amb un format clau-valor on cada parella hi és a una línia diferent, hi afegirem .writerWithDefaultPrettyPrinter() abans de fer writeValueAsString():
```
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Main {
public static void main(String[] args) throws JsonProcessingException{
Productes prod = new Productes(1,"Pomes",10,0.60);
String jsonString = new ObjectMapper()
.writerWithDefaultPrettyPrinter()
.writeValueAsString(prod);
System.out.println(jsonString);
}
}
```
Produint aquest output:
```
{
"idProd" : 1,
"descProd" : "Pomes",
"amountProd" : 10,
"unitPrice" : 0.6
}
```
Per últim, si volem escriure a un fitxer ho faríem així:
```
import java.io.File;
import java.io.IOException;
import com.fasterxml.jackson.core.exc.StreamWriteException;
import com.fasterxml.jackson.databind.DatabindException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Main {
public static void main(String[] args) throws StreamWriteException
, DatabindException, IOException {
Productes prod = new Productes(1,"Pomes",10,0.60);
ObjectMapper obmap = new ObjectMapper();
obmap.writerWithDefaultPrettyPrinter()
.writeValue(new File("products.json"), prod);
}
}
```
On si no fem servir writerWithDefaultPrettyPrinter() ens ho desarà com una línia per objecte. Quan ho executem veurem que ens crearà un arxiu products.json.
#### Procés de desserialització (JSON --> Objecte)
De forma similar, donat un objecte serialitzat amb JSON el podrem llegir i tractar. Vegem com.
Suposem que el JSON ve en un fitxer com el que hem acabat de generar:
```
import java.io.File;
import java.io.IOException;
import com.fasterxml.jackson.core.exc.StreamWriteException;
import com.fasterxml.jackson.databind.DatabindException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Main {
public static void main(String[] args) throws StreamWriteException
, DatabindException, IOException {
ObjectMapper obmap = new ObjectMapper();
Productes p1 = obmap.readValue(new File("products.json"), Productes.class);
System.out.print(p1);
}
}
```
El resultat d'executar aquest codi serà:
```
Productes [idProd=1, descProd=Pomes, amountProd=10, unitPrice=0.6]
```
Una altra possibilitat seria rebre l'objecte json en forma de cadena:
```
import java.io.IOException;
import com.fasterxml.jackson.core.exc.StreamWriteException;
import com.fasterxml.jackson.databind.DatabindException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Main {
public static void main(String[] args) throws StreamWriteException
, DatabindException, IOException {
ObjectMapper obmap = new ObjectMapper();
String json = "{\"idProd\":\"1\",\"descProd\":\"Pomes\","+
"\"amountProd\":\"10\",\"unitPrice\":\"0.6\"}";
Productes p1 = obmap.readValue(json,Productes.class);
System.out.print(p1);
}
}
```
### Bibliografia
* Cecilio Álvarez Caules: publicació a Arquitectura Java https://www.arquitecturajava.com/que-es-un-pojo-en-java/
* netjstech: serialization in java
https://www.netjstech.com/2017/04/serialization-in-java.html#JavaSerializationUse
* Baeldung: jackson object mapper tutorial https://www.baeldung.com/jackson-object-mapper-tutorial
<p xmlns:cc="http://creativecommons.org/ns#" xmlns:dct="http://purl.org/dc/terms/"><a property="dct:title" rel="cc:attributionURL" href="https://hackmd.io/@TTalens/SJbQ7LRk1g">Serialització amb java</a> by <a rel="cc:attributionURL dct:creator" property="cc:attributionName" href="https://hackmd.io/@TTalens">Toni Talens</a> is licensed under <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/?ref=chooser-v1" target="_blank" rel="license noopener noreferrer" style="display:inline-block;">Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International<img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/cc.svg?ref=chooser-v1" alt=""><img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/by.svg?ref=chooser-v1" alt=""><img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/nc.svg?ref=chooser-v1" alt=""><img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/sa.svg?ref=chooser-v1" alt=""></a></p>