"Hej kompis!"

"Hej, Diego."

"Jag ser här att du har lärt dig grunderna i JSON-serialisering?"

"Vad menar du med "grunderna"? Jag kan mycket!"

"Så naivt. Du vet inte hälften av det. Tio procent i bästa fall."

"Du skojar. Vad finns det mer?"

"Deserialisering av en objekthierarki (polymorf deserialisering), deserialisering av samlingar och en hel del mer! Jackson-ramverket är stort och kraftfullt. Ärligt talat, du har bara börjat skrapa på ytan."

"Okej, berätta för mig om det - jag är alla öron."

"Jag njuter verkligen av att bli smartare för varje lektion!"

"Ja, det är ett nöje att hjälpa till, min robotvän!"

"Är du redo? Lyssna då."

"Som du redan har lärt dig används annoteringar för både serialisering och deserialisering. I praktiken kräver serialisering mycket mindre information än deserialisering. Till exempel:"

Java klass JSON
class Cat
{
 public String name = "missy";
 public Cat[] cats = new Cat[0];
}
{
 "name": "missy",
 "cats": []
}
class Cat
{
 public String name = "missy";
 public List cats = new ArrayList<Cat>();
}
{
 "name": "missy",
 "cats": []
}
class Cat
{
 public String name = "missy";
 public List cats = new LinkedList<Cat>();
}
{
 "name": "missy",
 "cats": []
}

"Förekomster av Array, ArrayList, LinkedList och andra klasser omvandlas till JSON-arrayer."

"Men när du deserialiserar en JSON-array, vilket objekt ska du skapa: en ArrayList eller en LinkedList?"

"Rätt. Om en klassmedlem är ett gränssnitt (t.ex. public List<Cat> cats ), vilket objekt ska tillskrivas det?"

"Vi kan lägga till ytterligare kommentarer i fältet eller uttryckligen ange målklasserna under deserialisering. Titta på det här exemplet:"

Konvertera ett objekt från JSON
public static void main(String[] args) throws IOException
{
 String jsonString = ""{\"name\":\"Missy\",\"cats\":[{\"name\":\"Timmy\"},{\"name\":\"Killer\"}]}"";
 StringReader reader = new StringReader(jsonString);
 ObjectMapper mapper = new ObjectMapper();
 Cat cat = mapper.readValue(reader, TypeFactory.collectionType(ArrayList.class, Cat.class));
}
En klass vars objekt är deserialiserade från JSON
@JsonAutoDetect
class Cat {
 public String name;
 public List&ltCat> cats = new ArrayList<>();
 Cat() {
 }
}

"Med andra ord, vi kan använda den andra parametern i mappen . readValue -metoden för att skicka listan över klasser som ska användas under deserialisering."

"Jag gillar det. Det är bekvämt. Så du kan deserialisera en JSON-array till vad du än behöver, en ArrayList eller en LinkedList.

"Du nämnde också att du använder anteckningar. Hur gör du det?"

"Det är lätt. Till exempel:"

Konvertera ett objekt från JSON
public static void main(String[] args) throws IOException
{
 String jsonString = ""{\"name\":\"Missy\",\"cats\":[{\"name\":\"Timmy\"},{\"name\":\"Killer\"}]}"";
 StringReader reader = new StringReader(jsonString);

 ObjectMapper mapper = new ObjectMapper();

 Cat cat = mapper.readValue(reader, Cat.class);
}
En klass vars objekt är deserialiserade från JSON
@JsonAutoDetect
class Cat
{
 public String name;
 @JsonDeserialize(as = ArrayList.class, contentAs = Cat.class)
 public List&ltCat> cats = new ArrayList<>();
 Cat() {
 }
}

"Vi lägger helt enkelt till annoteringen @JsonDeserialize(as = ArrayList.class, contentAs = Cat.class) på rad 5 för att indikera vilken implementering av List-gränssnittet som ska användas."

"Ah. Jag förstår. Det är verkligen ganska enkelt."

"Men det finns mer. Anta att datatypen i en lista också är ett gränssnitt! Vad skulle du göra?"

"Använder vi en anteckning här också?"

"Ja, samma. Du kan också använda den för att ange parametertyp. Så här:"

Samlingstyp Hur man ställer in datatypen
Lista @JsonDeserialize(contentAs = ValueTypeImpl.class)
Karta @JsonDeserialize(keyAs = KeyTypeImpl.class)

"Coolt! Det behövs verkligen många kommentarer för olika situationer som vi inte kan förutse."

"Det är inte allt. Och det för oss till huvudrätten: i riktiga projekt ärver klasser ganska ofta samma basklass eller gränssnitt, som används praktiskt taget överallt. Och föreställ dig nu att du behöver deserialisera en datastruktur som innehåller sådana klasser. Till exempel:"

Konvertera ett objekt till JSON
public static void main(String[] args) throws IOException
{
 Cat cat = new Cat();
 cat.name = "Missy";
 cat.age = 5;

 Dog dog = new Dog();
 dog.name = "Killer";
 dog.age = 8;
 dog.owner = "Bill Jefferson";

 ArrayList<Pet> pets = new ArrayList<Pet>();
 pets.add(cat);
 pets.add(dog);

 StringWriter writer = new StringWriter();
 ObjectMapper mapper = new ObjectMapper();
 mapper.writeValue(writer, pets);
 System.out.println(writer.toString());
}
En klass vars objekt konverteras till JSON
@JsonAutoDetect
class Pet
{
 public String name;
}

@JsonAutoDetect
class Cat extends Pet
{
 public int age;
}

@JsonAutoDetect
class Dog extends Pet
{
 public int age;
 public String owner;
}
Serialiseringsresultat och skärmutgång:
[
 { "name" : "Missy", "age" : 5},
 { "name" : "Killer", "age" : 8 , "owner" : "Bill Jeferson"}
]

"Var uppmärksam på resultatet av serialisering."

"Vi kan inte deserialisera denna data till ett Java-objekt, eftersom den i princip inte går att skilja från data för andra klasser."

"Det finns några utmärkande egenskaper: Hund har ett ägarfält."

"Ja, men det här fältet kan vara null eller så kan det hoppas över helt under serialisering."

"Tja, kan vi inte specificera datatypen med anteckningar som vi känner till?"

"Nej. Efter deserialisering bör en enda samling ha olika katt- och hundföremål, samt ett dussin andra klasser som kan ärva från Pet."

"Vad i hela friden kan du göra här?"

"Här används två saker."

"Först väljs ett visst fält för att skilja en typ från en annan. Om det inte finns en så skapas den."

"För det andra finns det speciella anteckningar som låter dig styra processen för «polymorf deserialisering». Här är vad du kan göra:"

Konvertera ett objekt till JSON
public static void main(String[] args) throws IOException
{
 Cat cat = new Cat();
 cat.name = "Missy";
 cat.age = 5;

 Dog dog = new Dog();
 dog.name = "Killer";
 dog.age = 8;
 dog.owner = "Bill Jeferson";

 House house = new House();
 house.pets.add(dog);
 house.pets.add(cat);

 StringWriter writer = new StringWriter();
 ObjectMapper mapper = new ObjectMapper();
 mapper.writeValue(writer, house);
 System.out.println(writer.toString());
}
En klass vars objekt konverteras till JSON
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = Cat.class, name = "cat"),
@JsonSubTypes.Type(value = Dog.class, name = "dog")
})
class Pet
{
 public String name;
}

class Cat extends Pet
{
 public int age;
}

class Dog extends Pet
{
 public int age;
 public String owner;
}

class House
{
 public List&ltPet> pets = new ArrayList<>();
}
Serialiseringsresultat och skärmutgång:
{
 "pets" : [
 {"type" : "dog", "name" : "Killer", "age" : 8, "owner" : "Bill Jeferson"},
 {"type" : "cat", "name" : "Missy", "age" : 5}
]
}

Med hjälp av anteckningar indikerar vi att JSON-representationen kommer att innehålla ett speciellt fält som heter typ som kommer att innehålla värdet katt för klassen Cat och värdet hund för klassen Hund . Denna information är tillräcklig för att korrekt deserialisera ett objekt: under deserialiseringen kommer typen av objekt som ska skapas att bestämmas av värdet på typfältet.

"Ibland används klassnamnet som värdet för typfältet (t.ex. «com.example.entity.Cat.class»), men detta är inte en bra praxis. Hur skulle en extern applikation som tar emot vår JSON veta namnen på våra klasser? Ännu värre är att klasser ibland byter namn. Det är bättre att använda något unikt namn för att identifiera en specifik klass."

"Cool! Suck. Jag insåg inte att deserialisering var så komplicerad. Och att det finns så mycket man kan finjustera."

"Japp. Det här är nya koncept för dig, men det här är den typen av praktisk kunskap som kommer att göra dig till en geni programmerare."

"Amigo är en cool programmerare. Coolt!"

"OK. Gå och ta en paus."