Analyse av YAML med SnakeYAML

1. Oversikt

I denne opplæringen lærer vi hvordan du bruker SnakeYAML-biblioteket til serieisere Java-objekter til YAML-dokumenter og omvendt.

2. Prosjektoppsett

For å bruke SnakeYAML i prosjektet vårt, legger vi til følgende Maven-avhengighet (den siste versjonen finner du her):

 org.yaml snakeyaml 1.21 

3. Inngangspunkt

De Yaml klasse er inngangspunktet for API:

Yaml yaml = ny Yaml ();

Siden implementeringen ikke er trådsikker, må forskjellige tråder ha sin egen Yaml forekomst.

4. Laste inn et YAML-dokument

Biblioteket gir støtte for å laste dokumentet fra en String eller en InputStream. Flertallet av kodeeksemplene her vil være basert på å analysere InputStream.

La oss starte med å definere et enkelt YAML-dokument, og kalle filen som kunde.yaml:

fornavn: "John" etternavn: "Doe" alder: 20 år

4.1. Grunnleggende bruk

Nå analyserer vi YAML-dokumentet ovenfor med Yaml klasse:

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

Ovennevnte kode genererer følgende utdata:

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

Som standard er laste() metoden returnerer a Kart forekomst. Spørring av Kart objekt hver gang vil kreve at vi kjenner navnene på eiendomsnøklene på forhånd, og det er heller ikke lett å krysse over nestede eiendommer.

4.2. Egendefinert type

Biblioteket også gir en måte å laste dokumentet på som en tilpasset klasse. Dette alternativet vil tillate enkel gjennomgang av data i minnet.

La oss definere en Kunde klasse og prøv å laste dokumentet på nytt:

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

Forutsatt at YAML-dokumentet skal deserialiseres som en kjent type, kan vi spesifisere en eksplisitt global stikkord i dokumentet.

La oss oppdatere dokumentet og lagre det i en ny fil customer_with_type.yaml:

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

Legg merke til den første linjen i dokumentet, som inneholder informasjonen om klassen som skal brukes når du laster den.

Nå oppdaterer vi koden som brukes ovenfor, og sender det nye filnavnet som inndata:

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

De laste() metoden returnerer nå en forekomst av Kunde type. Ulempen med denne tilnærmingen er at typen må eksporteres som et bibliotek for å kunne brukes der det er nødvendig.

Selv om vi kan bruke den eksplisitte lokale koden som vi ikke er pålagt å eksportere biblioteker for.

En annen måte å laste inn en tilpasset type er ved å bruke Konstruktør klasse. På denne måten kan vi spesifisere rottypen for et YAML-dokument som skal analyseres. La oss lage en Konstruktør eksempel med Kunde skriv som rottype og send den til Yaml forekomst.

Nå når du laster inn customer.yaml, vi får Kunde gjenstand:

Yaml yaml = new Yaml (new Constructor (Customer.class));

4.3. Implisitte typer

Hvis det ikke er definert noen type for en gitt eiendom, konverterer biblioteket verdien automatisk til en implisitt type.

For eksempel:

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

La oss teste denne implisitte typen konvertering ved hjelp av en test case:

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

4.4. Nestede objekter og samlinger

Gitt en type på toppnivå, oppdager biblioteket automatisk hvilke typer nestede objekter, med mindre de er et grensesnitt eller en abstrakt klasse, og deserialiserer dokumentet til den relevante nestede typen.

La oss legge til Ta kontakt med og Adresse detaljer til customer.yaml, og lagre den nye filen som kunde_med_kontakt_detaljer_og_adresse.yaml.

Nå analyserer vi det nye YAML-dokumentet:

fornavn: "John" etternavn: "Doe" alder: 31 kontaktDetaljer: - type: "mobil" nummer: 123456789 - type: "fasttelefon" nummer: 456786868 hjemadresse: linje: "Xyz, DEF Street" by: "City Y" -stat : "State Y" zip: 345657 

Kunde klassen skal også gjenspeile disse endringene. Her er den oppdaterte klassen:

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

La oss se hvordan Ta kontakt med og Adresse klasser ser ut som:

offentlig klasse Kontakt {privat streng type; privat int nummer; // getters og setters}
offentlig klasse Adresse {privat strenglinje; private String city; privat strengstat; privat Integer zip; // getters og setters}

Nå skal vi teste Yaml#laste() med den gitte testsaken:

@Test offentlig ugyldig nårLoadYAMLDocumentWithTopLevelClass_thenLoadCorrectJavaObjectWithNestedObjects () {Yaml yaml = new Yaml (new Constructor (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 ("mobile", customer.getContactDetails () .get (0) .getType ()); assertEquals (123456789, customer.getContactDetails () .get (0) .getNumber ()); assertEquals ("fasttelefon", 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 trygge samlinger

Når en eller flere egenskaper for en gitt Java-klasse er typesikre (generiske) samlinger, er det viktig å spesifisere TypeBeskrivelse slik at riktig parameterisert type blir identifisert.

La oss ta en Kunde å ha mer enn en Ta kontakt med, og prøv å laste den inn:

fornavn: "John" etternavn: "Doe" alder: 31 contactDetails: - {type: "mobile", nummer: 123456789} - {type: "fasttelefon", nummer: 123456789}

For å laste inn dette dokumentet, vi kan spesifisere TypeBeskrivelse for den gitte eiendommen på toppnivåklassen:

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

4.6. Laster inn flere dokumenter

Det kan være tilfeller der, i en enkelt Fil Det er flere YAML-dokumenter, og vi vil analysere dem alle. De Yaml klasse gir en loadAll () metode for å gjøre en slik type analysering.

Som standard returnerer metoden en forekomst av Iterabel der hvert objekt er av typen Kart. Hvis du ønsker en tilpasset type, kan vi bruke Konstruktør eksempel som diskutert ovenfor.

Vurder følgende dokumenter i en enkelt fil:

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

Vi kan analysere ovennevnte ved hjelp av loadAll () metode som vist i nedenstående kodeeksempel:

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

5. Dumping av YAML-dokumenter

Biblioteket gir også en metode for å dump et gitt Java-objekt i et YAML-dokument. Utgangen kan være en String eller en spesifisert fil / strøm.

5.1. Grunnleggende bruk

Vi begynner med et enkelt eksempel på dumping av en forekomst av Kart til et YAML-dokument (String):

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

Ovennevnte kode produserer følgende utdata (merk at ved bruk av en forekomst av LinkedHashMap bevarer rekkefølgen på utdataene):

navn: Silenthand Olleander-løp: Menneskelige trekk: [ONE_HAND, ONE_EYE]

5.2. Egendefinerte Java-objekter

Vi kan også velge å dump egendefinerte Java-typer i en utgangsstrøm. Dette vil imidlertid legge til det globale eksplisitte stikkord til utdatadokumentet:

@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 (forventet Yaml, writer.toString ()); }

Med den ovennevnte tilnærmingen dumper vi fortsatt taginformasjonen i YAML-dokumentet.

Dette betyr at vi må eksportere klassen vår som et bibliotek for alle forbrukere som deserialiserer den. For å unngå kodenavnet i utdatafilen, kan vi bruke dumpAs () metode levert av biblioteket.

Så i koden ovenfor kan vi justere følgende for å fjerne koden:

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

6. Konklusjon

Denne artikkelen illustrerte bruk av SnakeYAML-biblioteket for å serieisere Java-objekter til YAML og omvendt.

Alle eksemplene finnes i GitHub-prosjektet - dette er et Maven-basert prosjekt, så det skal være enkelt å importere og kjøre som det er.


$config[zx-auto] not found$config[zx-overlay] not found