Introduktion til Moshi Json

1. Introduktion

I denne vejledning ser vi på Moshi, et moderne JSON-bibliotek til Java, der giver os kraftfuld JSON-serialisering og deserialisering i vores kode med lille indsats.

Moshi har en mindre API end andre biblioteker som Jackson eller Gson uden at gå på kompromis med funktionaliteten. Dette gør det lettere at integrere i vores applikationer og lader os skrive mere testbar kode. Det er også en mindre afhængighed, hvilket kan være vigtigt for visse scenarier - såsom udvikling til Android.

2. Tilføjelse af Moshi til vores bygning

Før vi kan bruge det, skal vi først tilføje Moshi JSON-afhængighederne til vores pom.xml fil:

 com.squareup.moshi moshi 1.9.2 com.squareup.moshi moshi-adaptere 1.9.2 

Det com.squareup.moshi: moshi afhængighed er hovedbiblioteket, og com.squareup.moshi: moshi-adaptere afhængighed er nogle standardtype adaptere - som vi vil udforske mere detaljeret senere.

3. Arbejde med Moshi og JSON

Moshi giver os mulighed for at konvertere alle Java-værdier til JSON og tilbage igen hvor som helst vi har brug for, uanset årsager - f.eks. til opbevaring af filer, skrivning af REST API'er, uanset hvilke behov vi måtte have.

Moshi arbejder med begrebet a JsonAdapter klasse. Dette er en typesafe-mekanisme til at serieisere en bestemt klasse til en JSON-streng og deserialisere en JSON-streng tilbage til den korrekte type:

offentlig klasse Post {privat streng titel; privat strengforfatter; privat strengtekst; // constructor, getters and setters} Moshi moshi = ny Moshi.Builder (). build (); JsonAdapter jsonAdapter = moshi.adapter (Post.class);

Når vi har bygget vores JsonAdapter, kan vi bruge det når som helst vi har brug for det for at konvertere vores værdier til JSON ved hjælp af toJson () metode:

Indlæg indlæg = nyt indlæg ("Mit indlæg", "Baeldung", "Dette er mit indlæg"); String json = jsonAdapter.toJson (post); // {"author": "Baeldung", "text": "Dette er mit indlæg", "title": "Mit indlæg"}

Og selvfølgelig kan vi konvertere JSON tilbage til de forventede Java-typer med den tilsvarende fraJson () metode:

Indlæg post = jsonAdapter.fromJson (json); // nyt indlæg ("Mit indlæg", "Baeldung", "Dette er mit indlæg");

4. Standard Java-typer

Moshi leveres med indbygget support til standard Java-typer, der konverteres til og fra JSON nøjagtigt som forventet. Dette dækker:

  • Alle primitiver - int, flyde, char, etc.
  • Alle Java-ækvivalenter - Heltal, flyde, karakter, etc.
  • Snor
  • Enums
  • Arrays af disse typer
  • Standard Java-samlinger af disse typer - Liste, sæt, kort

Ud over disse arbejder Moshi også automatisk med vilkårlige Java-bønner og konverterer dette til et JSON-objekt, hvor værdierne konverteres ved hjælp af de samme regler som enhver anden type. Dette betyder selvfølgelig, at Java-bønner inden for Java-bønner er korrekt serialiseret så dybt, som vi har brug for.

Det moshi-adaptere afhængighed giver os derefter adgang til nogle yderligere konverteringsregler, herunder:

  • En lidt mere kraftfuld adapter til Enums - understøtter en reserveværdi, når du læser en ukendt værdi fra JSON
  • En adapter til java.util.Date understøtter RFC-3339-formatet

Support til disse skal registreres hos en Moshi eksempel, inden de bruges. Vi ser dette nøjagtige mønster snart, når vi tilføjer support til vores egne tilpassede typer:

Moshi moshi = ny Moshi.builder () .add (ny Rfc3339DateJsonAdapter ()) .add (CurrencyCode.class, EnumJsonAdapter.create (CurrencyCode.class) .withUnknownFallback (CurrencyCode.USD)) .build ()

5. Brugerdefinerede typer i Moshi

Alt hidtil har givet os total support til serialisering og deserialisering af ethvert Java-objekt til JSON og tilbage. Men dette giver os ikke meget kontrol over, hvordan JSON ser ud, ved at serialisere Java-objekter ved bogstaveligt at skrive hvert felt i objektet som det er. Dette fungerer, men er ikke altid, hvad vi ønsker.

I stedet kan vi skrive vores egne adaptere til vores egne typer og have nøjagtig kontrol over, hvordan serialisering og deserialisering af disse typer fungerer.

5.1. Enkle konverteringer

Det enkle tilfælde er at konvertere mellem en Java-type og en JSON-for eksempel en streng. Dette kan være meget nyttigt, når vi skal repræsentere komplekse data i et specifikt format.

Forestil dig for eksempel, at vi har en Java-type, der repræsenterer forfatteren af ​​et indlæg:

public class Author {private String name; privat streng e-mail; // konstruktør, getters og setters}

Uden nogen anstrengelse overhovedet serieres dette som et JSON-objekt, der indeholder to felter - navn og e-mail. Vi vil dog serieisere det som en enkelt streng ved at kombinere navn og e-mail-adresse sammen.

Vi gør dette ved at skrive en standardklasse, der indeholder en metode, der er kommenteret @ToJson:

public class AuthorAdapter {@ToJson public String toJson (Author author) {return author.name + ""; }}

Det er klart, at vi også skal gå den anden vej. Vi er nødt til at analysere vores streng tilbage i vores Forfatter objekt. Dette gøres ved at tilføje en metode, der er kommenteret med @FromJson i stedet:

@FromJson public Author fromJson (String author) {Mønster mønster = Mønster.kompil ("^ (. *) $"); Matcher matcher = pattern.matcher (forfatter); returner matcher.find ()? ny forfatter (matcher.group (1), matcher.group (2)): null; }

Når det er gjort, skal vi rent faktisk bruge dette. Vi gør det på det tidspunkt, hvor vi opretter vores Moshi ved at tilføje adapteren til vores Moshi.Bygger:

Moshi moshi = ny Moshi.Builder () .add (ny AuthorAdapter ()) .build (); JsonAdapter jsonAdapter = moshi.adapter (Post.class);

Nu kan vi straks begynde at konvertere disse objekter til og fra JSON og få de ønskede resultater:

Indlæg indlæg = nyt indlæg ("Mit indlæg", ny forfatter ("Baeldung", "[e-mail-beskyttet]"), "Dette er mit indlæg"); String json = jsonAdapter.toJson (post); // {"author": "Baeldung <[email protected]>", "text": "Dette er mit indlæg", "title": "Mit indlæg"} Indlæg indlæg = jsonAdapter.fromJson (json); // nyt indlæg ("Mit indlæg", ny forfatter ("Baeldung", "[email protected]"), "Dette er mit indlæg");

5.2. Komplekse konverteringer

Disse konverteringer har været mellem Java bønner og JSON primitive typer. Vi kan også konvertere til struktureret JSON - i det væsentlige lade vi konvertere en Java-type til en anden struktur til gengivelse i vores JSON.

For eksempel har vi muligvis et behov for at gengive en dato / tid-værdi som tre forskellige værdier - datoen, klokkeslættet og tidszonen.

Ved hjælp af Moshi er alt, hvad vi skal gøre, at skrive en Java-type, der repræsenterer den ønskede output, og derefter vores @ToJson metode kan returnere dette nye Java-objekt, som Moshi derefter konverterer til JSON ved hjælp af dets standardregler:

offentlig klasse JsonDateTime {privat strengdato; privat streng tid; privat streng tidszone; // constructor, getters and setters} public class JsonDateTimeAdapter {@ToJson public JsonDateTime toJson (ZonedDateTime input) {String date = input.toLocalDate (). toString (); String time = input.toLocalTime (). ToString (); Strengtidzone = input.getZone (). Til String (); returner ny JsonDateTime (dato, tid, tidszone); }}

Som vi kan forvente, går den anden vej ved at skrive en @FromJson metode, der tager vores nye JSON-strukturerede type og returnerer den ønskede:

@FromJson public ZonedDateTime fromJson (JsonDateTime input) {LocalDate date = LocalDate.parse (input.getDate ()); LocalTime tid = LocalTime.parse (input.getTime ()); ZoneId tidszone = ZoneId.of (input.getTimezone ()); returnere ZonedDateTime.of (dato, tid, tidszone); }

Vi er så i stand til at bruge dette nøjagtigt som ovenfor til at konvertere vores ZonedDateTime ind i vores strukturerede output og tilbage:

Moshi moshi = ny Moshi.Builder () .add (ny JsonDateTimeAdapter ()) .build (); JsonAdapter jsonAdapter = moshi.adapter (ZonedDateTime.class); String json = jsonAdapter.toJson (ZonedDateTime.now ()); // {"date": "2020-02-17", "time": "07: 53: 27.064", "timezone": "Europe / London"} ZonedDateTime now = jsonAdapter.fromJson (json); // 2020-02-17T07: 53: 27.064Z [Europa / London]

5.3. Alternative adaptere

Nogle gange vil vi bruge en alternativ adapter til et enkelt felt i modsætning til at basere det på feltets type.

For eksempel kan vi have et enkelt tilfælde, hvor vi har brug for at gengive dato og tid som millisekunder fra epoken i stedet for som en ISO-8601-streng.

Moshi lader os gøre dette ved hjælp af en specielt kommenteret kommentar, som vi derefter kan anvende både på vores felt og vores adapter:

@Retention (RUNTIME) @Target ({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD}) @JsonQualifier offentlig @interface EpochMillis {}

Den centrale del af dette er @JsonQualifier kommentar, som gør det muligt for Moshi at binde alle felter, der er kommenteret med dette, til de passende adaptermetoder.

Dernæst skal vi skrive en adapter. Som altid har vi begge en @FromJson og en @ToJson metode til at konvertere mellem vores type og JSON:

public class EpochMillisAdapter {@ToJson public Long toJson (@EpochMillis Instant input) {return input.toEpochMilli (); } @FromJson @EpochMillis offentlig Øjeblikkelig fraJson (lang input) {returner Instant.ofEpochMilli (input); }}

Her har vi brugt vores kommentar til inputparameteren til @ToJson metode og om returværdien af @FromJson metode.

Moshi kan nu bruge denne adapter eller ethvert felt, der også er kommenteret @EpochMillis:

offentlig klasse Post {privat streng titel; privat strengforfatter; @EpochMillis Instant indsendt; // konstruktør, getters og setters}

Vi er nu i stand til at konvertere vores kommenterede type til JSON og tilbage efter behov:

Moshi moshi = ny Moshi.Builder () .add (ny EpochMillisAdapter ()) .build (); JsonAdapter jsonAdapter = moshi.adapter (Post.class); String json = jsonAdapter.toJson (nyt indlæg ("Introduktion til Moshi Json", "Baeldung", Instant.now ()); // {"author": "Baeldung", "posted": 1582095384793, "title": "Introduction to Moshi Json"} Post post = jsonAdapter.fromJson (json); // nyt indlæg ("Introduktion til Moshi Json", "Baeldung", Instant.now ())

6. Avanceret JSON-behandling

Nu hvor vi kan konvertere vores typer til JSON og tilbage, og vi kan kontrollere, hvordan denne konvertering sker. Der er nogle mere avancerede ting, som vi muligvis muligvis skal gøre med vores behandling, hvilket Moshi gør det let at opnå.

6.1. Omdøbning af JSON-felter

Nogle gange har vi brug for vores JSON for at have forskellige feltnavne end vores Java bønner. Dette kan være så simpelt som at ønske camelCase i Java og slangekuffert i JSON, eller det kan være at omdøbe feltet fuldstændigt for at matche det ønskede skema.

Vi kan bruge @Json kommentar for at give et nyt navn til ethvert felt i enhver bønne, som vi kontrollerer:

offentlig klasse Post {privat streng titel; @Json (name = "authored_by") privat strengforfatter; // konstruktør, getters og setters}

Når vi har gjort dette, forstår Moshi straks, at dette felt har et andet navn i JSON:

Moshi moshi = ny Moshi.Builder () .build (); JsonAdapter jsonAdapter = moshi.adapter (Post.class); Indlæg indlæg = nyt indlæg ("Mit indlæg", "Baeldung"); String json = jsonAdapter.toJson (post); // {"authored_by": "Baeldung", "title": "Mit indlæg"} Indlæg post = jsonAdapter.fromJson (json); // nyt indlæg ("Mit indlæg", "Baeldung")

6.2. Forbigående felter

I visse tilfælde har vi muligvis felter, der ikke skal medtages i JSON. Moshi bruger standarden forbigående kvalifikator for at angive, at disse felter ikke skal serieiseres eller deserialiseres:

offentlig statisk klasse Post {privat streng titel; privat forbigående Stringforfatter; // konstruktør, getters og setters}

Vi vil så se, at dette felt ignoreres fuldstændigt både ved serialisering og deserialisering:

Moshi moshi = ny Moshi.Builder () .build (); JsonAdapter jsonAdapter = moshi.adapter (Post.klasse); Indlæg indlæg = nyt indlæg ("Mit indlæg", "Baeldung"); String json = jsonAdapter.toJson (post); // {"title": "Mit indlæg"} Indlæg post = jsonAdapter.fromJson (json); // nyt indlæg ("Mit indlæg", null) Indlæg indlæg = jsonAdapter.fromJson ("{\" forfatter \ ": \" Baeldung \ ", \" titel \ ": \" Mit indlæg \ "}"); // nyt indlæg ("Mit indlæg", null)

6.3. Standardværdier

Nogle gange analyserer vi JSON, der ikke indeholder værdier for hvert felt i vores Java Bean. Dette er fint, og Moshi vil gøre sit bedste for at gøre det rigtige.

Moshi er ikke i stand til at bruge nogen form for argumentkonstruktør, når han deserialiserer vores JSON, men det er i stand til at bruge en no-args-konstruktør, hvis en er til stede.

Dette giver os derefter mulighed for at udfylde vores bønne, før JSON serialiseres, hvilket giver eventuelle krævede standardværdier til vores felter:

offentlig klasse Post {privat streng titel; privat strengforfatter; privat streng sendt; offentligt indlæg () {sendt = Instant.now (). toString (); } // getters og setters}

Hvis vores parsede JSON mangler titel eller forfatter felter ender disse med værdien nul. Hvis vi mangler sendt felt, vil dette i stedet have den aktuelle dato og tid:

Moshi moshi = ny Moshi.Builder () .build (); JsonAdapter jsonAdapter = moshi.adapter (Post.klasse); String json = "{\" title \ ": \" Mit indlæg \ "}"; Indlæg post = jsonAdapter.fromJson (json); // nyt indlæg ("Mit indlæg", null, "2020-02-19T07: 27: 01.141Z");

6.4. Analyse af JSON-arrays

Alt, hvad vi hidtil har gjort, har antaget, at vi serialiserer og deserialiserer et enkelt JSON-objekt til en enkelt Java-bønne. Dette er en meget almindelig sag, men det er ikke den eneste sag. Nogle gange vil vi også arbejde med samlinger af værdier, som er repræsenteret som en matrix i vores JSON.

Når arrayet er indlejret inde i vores bønner, er der intet at gøre. Moshi vil bare arbejde. Når hele JSON er en matrix, skal vi gøre mere arbejde for at opnå dette, simpelthen på grund af nogle begrænsninger i Java-generik. Vi er nødt til at konstruere vores JsonAdapter på en måde, som den ved, at den deserialiserer en generisk samling såvel som hvad samlingen er.

Moshi tilbyder nogle hjælp til at konstruere en java.lang.reflect.Type som vi kan levere til JsonAdapter når vi bygger det, så vi kan give disse yderligere generiske oplysninger:

Moshi moshi = ny Moshi.Builder () .build (); Type type = Types.newParameterizedType (List.class, String.class); JsonAdapter jsonAdapter = moshi.adapter (type);

Når dette er gjort, fungerer vores adapter nøjagtigt som forventet og respekterer disse nye generiske grænser:

String json = jsonAdapter.toJson (Arrays.asList ("One", "Two", "Three")); // ["One", "Two", "Three"] Listeresultat = jsonAdapter.fromJson (json); // Arrays.asList ("One", "Two", "Three");

7. Resume

Vi har set, hvordan Moshi-biblioteket kan gøre det nemt at konvertere Java-klasser til og fra JSON, og hvor fleksibelt det er. Vi kan bruge dette bibliotek overalt, hvor vi har brug for at konvertere mellem Java og JSON - hvad enten det er at indlæse og gemme fra filer, databasekolonner eller endda REST API'er. Hvorfor ikke prøve det?

Som normalt kan kildekoden til denne artikel findes på GitHub.