Analyse af YAML med SnakeYAML

1. Oversigt

I denne vejledning lærer vi, hvordan du bruger SnakeYAML-biblioteket til serialisere Java-objekter til YAML-dokumenter og omvendt.

2. Opsætning af projekt

For at bruge SnakeYAML i vores projekt tilføjer vi følgende Maven-afhængighed (den nyeste version kan findes her):

 org.yaml snakeyaml 1.21 

3. Indgangssted

Det Yaml klasse er adgangspunktet for API:

Yaml yaml = ny Yaml ();

Da implementeringen ikke er trådsikker, skal forskellige tråde have deres egne Yaml eksempel.

4. Indlæsning af et YAML-dokument

Biblioteket yder support til indlæsning af dokumentet fra en Snor eller en InputStream. Størstedelen af ​​kodeeksemplerne her ville være baseret på parsing af InputStream.

Lad os starte med at definere et simpelt YAML-dokument og navngive filen som kunde.yaml:

fornavn: "John" efternavn: "Doe" alder: 20

4.1. Grundlæggende brug

Nu analyserer vi ovenstående YAML-dokument med Yaml klasse:

Yaml yaml = ny Yaml (); InputStream inputStream = this.getClass () .getClassLoader () .getResourceAsStream ("customer.yaml"); Kort obj = yaml.load (inputStream); System.out.println (obj);

Ovenstående kode genererer følgende output:

{firstName = John, lastName = Doe, age = 20}

Som standard er belastning() metode returnerer a Kort eksempel. Forespørgsel på Kort objekt hver gang vil kræve, at vi kender ejendomsnøglenavnene på forhånd, og det er heller ikke let at krydse over indlejrede egenskaber.

4.2. Brugerdefineret type

Biblioteket også giver en måde at indlæse dokumentet på som en brugerdefineret klasse. Denne mulighed gør det let at krydse data i hukommelsen.

Lad os definere en Kunde klasse og prøv at indlæse dokumentet igen:

offentlig klasse kunde {privat streng fornavn; privat streng efternavn; privat int alder // getters og setters}

Antages det, at YAML-dokumentet deserialiseres som en kendt type, kan vi specificere et eksplicit globalt tag i dokumentet.

Lad os opdatere dokumentet og gemme det i en ny fil kunde_med_type.yaml:

!! com.baeldung.snakeyaml.Customer firstName: "John" efternavn: "Doe" alder: 20

Bemærk den første linje i dokumentet, som indeholder oplysningerne om den klasse, der skal bruges, når den indlæses.

Nu opdaterer vi koden, der er brugt ovenfor, og sender det nye filnavn som input:

Yaml yaml = ny Yaml (); InputStream inputStream = this.getClass () .getClassLoader () .getResourceAsStream ("yaml / customer_with_type.yaml"); Kundekunde = yaml.load (inputStream); 

Det belastning() metoden returnerer nu en forekomst af Kunde type. Ulempen ved denne tilgang er, at typen skal eksporteres som et bibliotek for at blive brugt, hvor det er nødvendigt.

Selvom vi kunne bruge det eksplicitte lokale tag, som vi ikke er forpligtet til at eksportere biblioteker for.

En anden måde at indlæse en brugerdefineret type på er ved hjælp af Konstruktør klasse. På denne måde kan vi specificere rodtypen for et YAML-dokument, der skal parses. Lad os oprette en Konstruktør eksempel med Kunde skriv som rodtype og send den til Yaml eksempel.

Nu ved indlæsning af kunde.yaml, vi får den Kunde objekt:

Yaml yaml = ny Yaml (ny konstruktør (kunde.klasse));

4.3. Implicitte typer

Hvis der ikke er defineret nogen type for en given egenskab, konverterer biblioteket automatisk værdien til en implicit type.

For eksempel:

1.0 -> Float 42 -> Heltal 2009-03-30 -> Dato

Lad os teste denne implicit type konvertering ved hjælp af en test case:

@Test offentlig ugyldig nårLoadYAML_thenLoadCorrectImplicitTypes () {Yaml yaml = ny Yaml (); Kortdokument = yaml.load ("3.0: 2018-07-22"); assertNotNull (dokument); assertEquals (1, document.size ()); assertTrue (document.containsKey (3.0d)); }

4.4. Indlejrede objekter og samlinger

Givet en type på øverste niveau, registrerer biblioteket automatisk typerne af indlejrede objekter, medmindre de er en grænseflade eller en abstrakt klasse og deserialiserer dokumentet til den relevante indlejrede type.

Lad os tilføje Kontakt og Adresse detaljer til kunde.yaml, og gem den nye fil som kunde_med_kontakt_detaljer_og_adresse.yaml.

Nu analyserer vi det nye YAML-dokument:

fornavn: "John" efternavn: "Doe" alder: 31 kontaktDetaljer: - type: "mobil" nummer: 123456789 - type: "fastnet" nummer: 456786868 hjemAdresse: linje: "Xyz, DEF Street" by: "By Y" tilstand : "State Y" zip: 345657 

Kunde klasse skal også afspejle disse ændringer. Her er den opdaterede klasse:

offentlig klasse kunde {privat streng fornavn; privat streng efternavn; privat int alder privat liste contactDetails; privat adresse hjem Adresse; // getters og setters} 

Lad os se hvordan Kontakt og Adresse klasser ser ud som:

offentlig klasse Kontakt {privat streng type; privat int-nummer; // getters og setters}
offentlig klasse Adresse {privat strenglinje; private String by; privat strengstat; privat Heltals lynlås; // getters og setters}

Nu tester vi Yaml#belastning() med den givne test case:

@Test offentlig ugyldig nårLoadYAMLDocumentWithTopLevelClass_thenLoadCorrectJavaObjectWithNestedObjects () {Yaml yaml = ny Yaml (ny konstruktør (Customer.class)); InputStream inputStream = this.getClass () .getClassLoader () .getResourceAsStream ("yaml / customer_with_contact_details_and_address.yaml"); Kundekunde = yaml.load (inputStream); assertNotNull (kunde); assertEquals ("John", customer.getFirstName ()); assertEquals ("Doe", customer.getLastName ()); assertEquals (31, customer.getAge ()); assertNotNull (customer.getContactDetails ()); assertEquals (2, customer.getContactDetails (). størrelse ()); assertEquals ("mobil", customer.getContactDetails () .get (0) .getType ()); assertEquals (123456789, customer.getContactDetails () .get (0) .getNumber ()); assertEquals ("fastnet", customer.getContactDetails () .get (1) .getType ()); assertEquals (456786868, customer.getContactDetails () .get (1) .getNumber ()); assertNotNull (customer.getHomeAddress ()); assertEquals ("Xyz, DEF Street", customer.getHomeAddress () .getLine ()); }

4.5. Type-sikre samlinger

Når en eller flere egenskaber for en given Java-klasse er typesikre (generiske) samlinger, er det vigtigt at specificere TypeBeskrivelse så den korrekte parametriserede type identificeres.

Lad os tage en Kunde har mere end en Kontakt, og prøv at indlæse det:

firstName: "John" efternavn: "Doe" alder: 31 contactDetails: - {type: "mobile", nummer: 123456789} - {type: "fastnet", nummer: 123456789}

For at indlæse dette dokument, vi kan specificere TypeBeskrivelse for den givne ejendom på øverste niveau klasse:

Konstruktør konstruktør = ny konstruktør (Customer.class); TypeDescription customTypeDescription = ny TypeDescription (Customer.class); customTypeDescription.addPropertyParameters ("contactDetails", Contact.class); constructor.addTypeDescription (customTypeDescription); Yaml yaml = ny Yaml (konstruktør);

4.6. Indlæser flere dokumenter

Der kan være tilfælde, hvor i en enkelt Fil der er flere YAML-dokumenter, og vi vil analysere dem alle. Det Yaml klasse giver en loadAll () metode til at udføre en sådan type parsing.

Som standard returnerer metoden en forekomst af Iterabel hvor hvert objekt er af typen Kort. Hvis en ønsket type ønskes, kan vi bruge Konstruktør eksempel som beskrevet ovenfor.

Overvej følgende dokumenter i en enkelt fil:

--- fornavn: "John" efternavn: "Doe" alder: 20 --- fornavn: "Jack" efternavn: "Jones" alder: 25

Vi kan analysere ovenstående ved hjælp af loadAll () metode som vist i nedenstående kodeeksempel:

@Test offentlig ugyldig nårLoadMultipleYAMLDocuments_thenLoadCorrectJavaObjects () {Yaml yaml = ny Yaml (ny konstruktør (Customer.class)); InputStream inputStream = this.getClass () .getClassLoader () .getResourceAsStream ("yaml / customers.yaml"); int-antal = 0; for (Objektobjekt: yaml.loadAll (inputStream)) {count ++; assertTrue (objektforekomst af kunde); } assertEquals (2, count); }

5. Dumping af YAML-dokumenter

Biblioteket giver også en metode til dump et givet Java-objekt i et YAML-dokument. Outputtet kunne være en Snor eller en bestemt fil / stream.

5.1. Grundlæggende brug

Vi starter med et simpelt eksempel på dumping af en forekomst af Kort til et YAML-dokument (Snor):

@Test offentlig ugyldig nårDumpMap_thenGenerateCorrectYAML () {Map data = new LinkedHashMap (); data.put ("navn", "Silenthand Olleander"); data.put ("race", "Human"); data.put ("træk", ny streng [] {"ONE_HAND", "ONE_EYE"}); Yaml yaml = ny Yaml (); StringWriter-forfatter = ny StringWriter (); yaml.dump (data, forfatter); Streng forventetYaml = "navn: Silenthand Olleander \ nrace: Human \ negenskaber: [ONE_HAND, ONE_EYE] \ n"; assertEquals (expectedYaml, writer.toString ()); }

Ovenstående kode producerer følgende output (bemærk at ved hjælp af en forekomst af LinkedHashMap bevarer rækkefølgen af ​​outputdataene):

navn: Silenthand Olleander race: Menneskelige træk: [ONE_HAND, ONE_EYE]

5.2. Brugerdefinerede Java-objekter

Vi kan også vælge at dump tilpassede Java-typer i en output-stream. Dette vil dog tilføje det globale eksplicit tag til outputdokumentet:

@Test offentlig ugyldig nårDumpACustomType_thenGenerateCorrectYAML () {Kundekunde = ny kunde (); customer.setAge (45); customer.setFirstName ("Greg"); customer.setLastName ("McDowell"); Yaml yaml = ny Yaml (); StringWriter-forfatter = ny StringWriter (); yaml.dump (kunde, forfatter); String expectYaml = "!! com.baeldung.snakeyaml.Customer {age: 45, contactDetails: null, firstName: Greg, \ n homeAddress: null, lastName: McDowell} \ n"; assertEquals (expectYaml, writer.toString ()); }

Med ovenstående tilgang dumper vi stadig tagoplysningerne i YAML-dokumentet.

Dette betyder, at vi er nødt til at eksportere vores klasse som et bibliotek til enhver forbruger, der deserialiserer den. For at undgå tagnavnet i outputfilen kan vi bruge dumpAs () metode leveret af biblioteket.

Så i ovenstående kode kunne vi tilpasse følgende for at fjerne tagget:

yaml.dumpAs (kunde, Tag.MAP, null);

6. Konklusion

Denne artikel illustrerede anvendelser af SnakeYAML-biblioteket til serialisering af Java-objekter til YAML og omvendt.

Alle eksemplerne findes i GitHub-projektet - dette er et Maven-baseret projekt, så det skal være let at importere og køre som det er.