Uvod u Moshi Json

1. Uvod

U ovom uputstvu ćemo pogledati Moshi, modernu JSON knjižnicu za Javu koja će nam pružiti snažnu JSON serializaciju i deserializaciju u našem kodu s malo truda.

Moshi ima manji API od ostalih knjižnica kao što su Jackson ili Gson, bez ugrožavanja funkcionalnosti. To olakšava integraciju u naše aplikacije i omogućuje nam pisanje više provjerljivog koda. To je također manja ovisnost, što može biti važno za određene scenarije - poput razvoja za Android.

2. Dodavanje Moshija našoj gradnji

Prije nego što ga upotrijebimo, prvo moramo dodati Moshi JSON ovisnosti u našu pom.xml datoteka:

 com.squareup.moshi moshi 1.9.2 com.squareup.moshi moshi-adapteri 1.9.2 

The com.squareup.moshi: moshi ovisnost je glavna knjižnica, a com.squareup.moshi: moshi-adapteri ovisnost su neki standardni adapter tipa - koje ćemo detaljnije istražiti kasnije.

3. Suradnja s Moshi i JSON-om

Moshi nam omogućuje pretvaranje bilo kojih Java vrijednosti u JSON i natrag bilo gdje trebamo iz bilo kojih razloga - npr. za pohranu datoteka, pisanje REST API-ja, bez obzira na potrebe.

Moshi radi s konceptom a JsonAdapter razred. Ovo je sigurnosni mehanizam za serializaciju određene klase u JSON niz i za deserializaciju JSON niza natrag u ispravan tip:

javna klasa Post {private String title; privatni autor niza; privatni tekst niza; // konstruktor, getteri i postavljači} Moshi moshi = novi Moshi.Builder (). build (); JsonAdapter jsonAdapter = moshi.adapter (Post.class);

Jednom kada smo izgradili svoj JsonAdapter, možemo ga koristiti kad god je potrebno kako bismo pretvorili svoje vrijednosti u JSON pomoću toJson () metoda:

Objavi post = novi post ("Moj post", "Baeldung", "Ovo je moj post"); Niz json = jsonAdapter.toJson (post); // {"author": "Baeldung", "text": "Ovo je moj post", "title": "My Post"}

I, naravno, možemo pretvoriti JSON natrag u očekivane vrste Java s odgovarajućim odJson () metoda:

Objavi post = jsonAdapter.fromJson (json); // novi post ("Moj post", "Baeldung", "Ovo je moj post");

4. Standardni Java tipovi

Moshi dolazi s ugrađenom podrškom za standardne tipove Java, pretvarajući u i iz JSON-a točno onako kako se očekivalo. Ovo obuhvaća:

  • Svi primitivci - int, float, charitd.
  • Svi ekvivalenti u paketu Java - Cijeli broj, plutajući, likitd.
  • Niz
  • Enum
  • Nizovi ovih vrsta
  • Standardne Java kolekcije ovih vrsta - Popis, Postavljanje, Karta

Uz njih, Moshi će također automatski raditi sa bilo kojim proizvoljnim Java grahom, pretvarajući ga u JSON objekt gdje se vrijednosti pretvaraju koristeći ista pravila kao i bilo koja druga vrsta. To očito znači da su Java grah unutar Java graha ispravno serializiran onoliko duboko koliko trebamo ići.

The moshi-adapteri ovisnost nam daje pristup nekim dodatnim pravilima pretvorbe, uključujući:

  • Nešto snažniji adapter za Enums - podržava zamjensku vrijednost pri čitanju nepoznate vrijednosti iz JSON-a
  • Adapter za java.util.Datum koji podržava format RFC-3339

Podršku za njih treba registrirati kod Moshi primjer prije nego što će se upotrijebiti. Upravo ćemo vidjeti taj uzorak kad dodamo podršku za vlastite prilagođene vrste:

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

5. Prilagođene vrste u Moshi

Sve do sada dalo nam je potpunu podršku za serializaciju i deserializaciju bilo kojeg Java objekta u JSON i natrag. Ali ovo nam ne daje veliku kontrolu nad izgledom JSON-a, serializirajući Java objekte doslovnim upisivanjem svakog polja u objekt kakav jest. To djeluje, ali nije uvijek ono što želimo.

Umjesto toga, možemo napisati vlastite adaptere za vlastite tipove i imati točnu kontrolu nad funkcioniranjem serializacije i deserializacije tih tipova.

5.1. Jednostavne pretvorbe

Jednostavni slučaj je pretvorba između tipa Java i JSON - na primjer niz. To može biti vrlo korisno kada moramo predstaviti složene podatke u određenom formatu.

Na primjer, zamislimo da imamo Java tip koji predstavlja autora posta:

autor javne klase {naziv privatnog niza; privatni String e-mail; // konstruktor, getteri i postavljači}

Bez ikakvog napora ovo će se serijalizirati kao JSON objekt koji sadrži dva polja - Ime i e-mail. Ipak ga želimo serializirati kao jedan niz, kombinirajući ime i adresu e-pošte.

To radimo pisanjem standardne klase koja sadrži metodu označenu s @ToJson:

javna klasa AuthorAdapter {@ToJson javni niz toJson (autor autora) {return author.name + ""; }}

Očito je da moramo ići i drugim putem. Moramo raščlaniti naš niz natrag u naš Autor objekt. To se postiže dodavanjem metode označene s @FromJson umjesto toga:

@FromJson javni autor fromJson (Autor niza) {Pattern pattern = Pattern.compile ("^ (. *) $"); Podudaranje podudaranja = pattern.matcher (autor); vratiti podudaranje.naći ()? novi autor (matcher.group (1), matcher.group (2)): null; }

Kad završimo, ovo zapravo moramo iskoristiti. To radimo u vrijeme kad stvaramo svoje Moshi dodavanjem adaptera na naš Moshi.Builder:

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

Sada možemo odmah početi pretvarati ove objekte u i iz JSON-a i dobiti rezultate koje smo željeli:

Objavi post = novi post ("Moj post", novi autor ("Baeldung", "[e-pošta zaštićena]"), "Ovo je moj post"); Niz json = jsonAdapter.toJson (post); // {"author": "Baeldung <[email protected]>", "text": "Ovo je moj post", "title": "My Post"} Objavi post = jsonAdapter.fromJson (json); // novi post ("Moj post", novi autor ("Baeldung", "[e-pošta zaštićena]"), "Ovo je moj post");

5.2. Složene konverzije

Te su konverzije izvršene između Java graha i JSON primitivnih tipova. Također možemo pretvoriti u strukturirani JSON - u osnovi dopuštajući nam da pretvorimo tip Java u drugu strukturu za prikazivanje u našem JSON-u.

Na primjer, možda ćemo trebati prikazati vrijednost datuma / vremena kao tri različite vrijednosti - datum, vrijeme i vremensku zonu.

Koristeći Moshi, sve što trebamo je napisati Java tip koji predstavlja željeni izlaz, a zatim naš @ToJson metoda može vratiti ovaj novi Java objekt, koji će Moshi pretvoriti u JSON koristeći svoja standardna pravila:

javna klasa JsonDateTime {datum privatnog niza; privatno vrijeme gudača; privatna vremenska zona String; // konstruktor, getteri i postavljači} javna klasa JsonDateTimeAdapter {@ToJson javni JsonDateTime toJson (ZonedDateTime input) {Datum niza = input.toLocalDate (). toString (); Vrijeme niza = input.toLocalTime (). ToString (); Niz vremenske zone = input.getZone (). ToString (); vrati novi JsonDateTime (datum, vrijeme, vremenska zona); }}

Kao što možemo očekivati, prelazak na drugi način obavlja se pisanjem znaka @FromJson metoda koja uzima naš novi JSON strukturirani tip i vraća našu željenu:

@FromJson public ZonedDateTime fromJson (JsonDateTime input) {LocalDate date = LocalDate.parse (input.getDate ()); LocalTime vrijeme = LocalTime.parse (input.getTime ()); ZoneId vremenska zona = ZoneId.of (input.getTimezone ()); vrati ZonedDateTime.of (datum, vrijeme, vremenska zona); }

Tada smo u mogućnosti koristiti ovo točno kao gore za pretvaranje našeg ZonedDateTime u naš strukturirani izlaz i natrag:

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

5.3. Adapteri alternativnog tipa

Ponekad želimo koristiti alternativni adapter za jedno polje, za razliku od toga da ga temeljimo na vrsti polja.

Na primjer, možemo imati jedan slučaj u kojem trebamo prikazati datum i vrijeme u milisekundama od epohe umjesto kao niz ISO-8601.

Moshi nam omogućuje da to učinimo pomoću posebno označene bilješke koju zatim možemo primijeniti i na svoje polje i na svoj adapter:

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

Ključni dio ovoga je @JsonQualifier napomena, koja Moshiju omogućuje da sva polja koja su s tim označena poveže s odgovarajućim načinima prilagodnika.

Dalje, trebamo napisati adapter. Kao i uvijek imamo oboje a @FromJson i a @ToJson metoda za pretvorbu između našeg tipa i JSON-a:

javna klasa EpochMillisAdapter {@ToJson public Long toJson (@EpochMillis Instant input) {return input.toEpochMilli (); } @FromJson @EpochMillis javni Instant fromJson (Dugi ulaz) {return Instant.ofEpochMilli (ulaz); }}

Ovdje smo upotrijebili našu napomenu na ulaznom parametru za @ToJson metodu i na povratnu vrijednost @FromJson metoda.

Moshi sada može koristiti ovaj adapter ili bilo koje polje koje je također označeno @EpochMillis:

javna klasa Post {private String title; privatni autor niza; @EpochMillis Instant objavljeno; // konstruktor, getteri i postavljači}

Sada smo u mogućnosti pretvoriti svoj označeni tip u JSON i natrag po potrebi:

Moshi moshi = novi Moshi.Builder () .add (novi EpochMillisAdapter ()) .build (); JsonAdapter jsonAdapter = moshi.adapter (Post.class); Niz json = jsonAdapter.toJson (novi post ("Uvod u Moshi Json", "Baeldung", Instant.now ())); // {"author": "Baeldung", "posted": 1582095384793, "title": "Uvod u Moshi Json"} Post post = jsonAdapter.fromJson (json); // novi post ("Uvod u Moshi Json", "Baeldung", Instant.now ())

6. Napredna JSON obrada

Sad kad svoje vrste možemo pretvoriti u JSON i natrag, i možemo kontrolirati način na koji se ta pretvorba događa. Postoje neke naprednije stvari koje ćemo možda morati učiniti povremeno s našom obradom, što Moshi olakšava postizanje.

6.1. Preimenovanje JSON polja

Ponekad nam je potreban da naš JSON ima različita imena polja u odnosu na naš Java grah. Ovo može biti jednostavno kao što želite camelCase u Javi i kutija zmija u JSON-u ili bi moglo biti potpuno preimenovati polje tako da odgovara željenoj shemi.

Možemo koristiti @Json napomena za davanje novog imena bilo kojem polju u bilo kojem grahu koji kontroliramo:

javna klasa Post {private String title; @Json (name = "authored_by") autor privatnog niza; // konstruktor, getteri i postavljači}

Kad smo to učinili, Moshi odmah shvaća da ovo polje ima drugačije ime u JSON-u:

Moshi moshi = novi Moshi.Builder () .build (); JsonAdapter jsonAdapter = moshi.adapter (Post.class); Objavi post = novi post ("Moj post", "Baeldung"); Niz json = jsonAdapter.toJson (post); // {"authored_by": "Baeldung", "title": "My Post"} Objavi post = jsonAdapter.fromJson (json); // novi post ("Moj post", "Baeldung")

6.2. Prolazna polja

U određenim slučajevima možemo imati polja koja ne bi trebala biti uključena u JSON. Moshi koristi standard prolazan kvalifikator koji označava da se ova polja ne smiju serializirati ili deserijalizirati:

javna statička klasa Post {naslov privatnog niza; privatni privremeni autor niza; // konstruktor, getteri i postavljači}

Tada ćemo vidjeti da se ovo polje u potpunosti zanemaruje i prilikom serializacije i deserializacije:

Moshi moshi = novi Moshi.Builder () .build (); JsonAdapter jsonAdapter = moshi.adapter (Post.class); Objavi post = novi post ("Moj post", "Baeldung"); Niz json = jsonAdapter.toJson (post); // {"title": "My Post"} Objavi post = jsonAdapter.fromJson (json); // novi post ("Moj post", null) Post post = jsonAdapter.fromJson ("{\" autor \ ": \" Baeldung \ ", \" naslov \ ": \" Moj post \ "}"); // novi post ("Moj post", null)

6.3. Zadane vrijednosti

Ponekad raščlanjujemo JSON koji ne sadrži vrijednosti za svako polje u našem Java Beanu. To je u redu i Moshi će se potruditi učiniti ispravnu stvar.

Moshi nije u mogućnosti koristiti bilo koji oblik konstruktora argumenata prilikom deserializacije našeg JSON-a, ali može koristiti konstruktor no-args ako je prisutan.

To će nam omogućiti da unaprijed popunimo naš grah prije nego što se JSON serializira, dajući sve potrebne zadane vrijednosti našim poljima:

javna klasa Post {private String title; privatni autor niza; privatni niz objavljen; javni post () {posted = Instant.now (). toString (); } // geteri i postavljači}

Ako našem raščlanjenom JSON-u nedostaje titula ili Autor polja će onda završiti s vrijednošću null. Ako nam nedostaje objavljeno polje će umjesto toga imati trenutni datum i vrijeme:

Moshi moshi = novi Moshi.Builder () .build (); JsonAdapter jsonAdapter = moshi.adapter (Post.class); Niz json = "{\" title \ ": \" Moj post \ "}"; Objavi post = jsonAdapter.fromJson (json); // novi post ("Moja pošta", null, "2020-02-19T07: 27: 01.141Z");

6.4. Raščlanjivanje JSON nizova

Sve što smo do sada radili pretpostavljalo je da serializiramo i deserializiramo jedan JSON objekt u jedan Java zrno. Ovo je vrlo čest slučaj, ali nije jedini slučaj. Ponekad želimo raditi i sa zbirkama vrijednosti, koje su predstavljene kao niz u našem JSON-u.

Kad je niz ugniježđen unutar našeg graha, nema se što učiniti. Moshi će samo raditi. Kada je čitav JSON niz, tada moramo učiniti više posla da bismo to postigli, jednostavno zbog nekih ograničenja u generičkim programima Java. Moramo konstruirati svoje JsonAdapter na način da zna da deserijalizira generičku zbirku, kao i što je zbirka.

Moshi nudi pomoć u konstrukciji a java.lang.reflect.Type koje možemo pružiti JsonAdapter kada ga gradimo tako da možemo pružiti ove dodatne generičke informacije:

Moshi moshi = novi Moshi.Builder () .build (); Tip tipa = Types.newParameterizedType (List.class, String.class); JsonAdapter jsonAdapter = moshi.adapter (vrsta);

Kad se to učini, naš adapter radi točno onako kako se očekivalo, poštujući ove nove generičke granice:

Niz json = jsonAdapter.toJson (Arrays.asList ("Jedan", "Dva", "Tri")); // ["Jedan", "Dva", "Tri"] Rezultat popisa = jsonAdapter.fromJson (json); // Arrays.asList ("Jedan", "Dva", "Tri");

7. Sažetak

Vidjeli smo kako knjižnica Moshi može pretvoriti Java klase u i iz JSON-a doista jednostavno i koliko je fleksibilna. Ovu knjižnicu možemo koristiti bilo gdje koje treba pretvoriti između Jave i JSON-a - bilo da se radi o učitavanju i spremanju iz datoteka, stupaca baze podataka ili čak REST API-ja. Zašto ga ne isprobati?

Kao i obično, izvorni kod za ovaj članak može se naći na GitHubu.