Inleiding tot Moshi Json

1. Inleiding

In deze tutorial zullen we Moshi bekijken, een moderne JSON-bibliotheek voor Java die ons met weinig moeite krachtige JSON-serialisatie en deserialisatie in onze code zal geven.

Moshi heeft een kleinere API dan andere bibliotheken zoals Jackson of Gson zonder in te leveren op functionaliteit. Dit maakt het gemakkelijker om te integreren in onze applicaties en stelt ons in staat om meer testbare code te schrijven. Het is ook een kleinere afhankelijkheid, wat belangrijk kan zijn voor bepaalde scenario's - zoals ontwikkelen voor Android.

2. Moshi aan ons gebouw toevoegen

Voordat we het kunnen gebruiken, moeten we eerst de Moshi JSON-afhankelijkheden toevoegen aan onze pom.xml het dossier:

 com.squareup.moshi moshi 1.9.2 com.squareup.moshi moshi-adapters 1.9.2 

De com.squareup.moshi: moshi afhankelijkheid is de hoofdbibliotheek en de com.squareup.moshi: moshi-adapters afhankelijkheid is een aantal standaard type adapters - die we later in meer detail zullen onderzoeken.

3. Werken met Moshi en JSON

Met Moshi kunnen we alle Java-waarden converteren naar JSON en weer terug waar we maar willen om welke reden dan ook, bijvoorbeeld voor bestandsopslag, het schrijven van REST API's, welke behoeften we ook hebben.

Moshi werkt met het concept van een JsonAdapter klasse. Dit is een typeveilig mechanisme om een ​​specifieke klasse te serialiseren in een JSON-string en om een ​​JSON-string terug te deserialiseren naar het juiste type:

openbare klasse Post {privé String-titel; private String-auteur; private String-tekst; // constructor, getters and setters} Moshi moshi = nieuwe Moshi.Builder (). build (); JsonAdapter jsonAdapter = moshi.adapter (Post.class);

Zodra we onze hebben gebouwd JsonAdapter, kunnen we het gebruiken wanneer dat nodig is om onze waarden naar JSON te converteren met behulp van de toJson () methode:

Post post = nieuwe post ("Mijn post", "Baeldung", "Dit is mijn post"); String json = jsonAdapter.toJson (bericht); // {"author": "Baeldung", "text": "Dit is mijn bericht", "title": "Mijn bericht"}

En natuurlijk kunnen we JSON terug converteren naar de verwachte Java-typen met de bijbehorende van Json () methode:

Post post = jsonAdapter.fromJson (json); // nieuw bericht ("Mijn bericht", "Baeldung", "Dit is mijn bericht");

4. Standaard Java-typen

Moshi wordt geleverd met ingebouwde ondersteuning voor standaard Java-typen, die precies zoals verwacht van en naar JSON converteren. Dit omvat:

  • Alle primitieven - int, float, char, enz.
  • Alle Java-boxed equivalenten - Geheel getal, Float, Character, enz.
  • Draad
  • Enums
  • Arrays van deze typen
  • Standaard Java-verzamelingen van deze typen - Lijst, set, kaart

Naast deze werkt Moshi ook automatisch met elke willekeurige Java-bean en converteert deze naar een JSON-object waar de waarden worden geconverteerd met dezelfde regels als elk ander type. Dit betekent uiteraard dat Java-bonen in Java-bonen correct zijn geserialiseerd zo diep als we nodig hebben.

De moshi-adapters afhankelijkheid geeft ons dan toegang tot enkele aanvullende conversieregels, waaronder:

  • Een iets krachtigere adapter voor Enums - ondersteunt een terugvalwaarde bij het lezen van een onbekende waarde uit de JSON
  • Een adapter voor java.util.Date ondersteunt het RFC-3339-formaat

Ondersteuning hiervoor moet worden geregistreerd met een Moshi instantie voordat ze worden gebruikt. We zullen dit exacte patroon binnenkort zien wanneer we ondersteuning toevoegen voor onze eigen aangepaste typen:

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

5. Aangepaste typen in Moshi

Alles tot dusver heeft ons volledige ondersteuning gegeven voor het serialiseren en deserialiseren van elk Java-object naar JSON en terug. Maar dit geeft ons niet veel controle over hoe de JSON eruitziet, door Java-objecten te serialiseren door letterlijk elk veld in het object te schrijven zoals het is. Dit werkt, maar is niet altijd wat we willen.

In plaats daarvan kunnen we onze eigen adapters voor onze eigen typen schrijven en hebben we exacte controle over hoe de serialisatie en deserialisatie van deze typen werkt.

5.1. Eenvoudige conversies

Het simpele geval is het converteren tussen een Java-type en een JSON-type - bijvoorbeeld een string. Dit kan erg handig zijn wanneer we complexe gegevens in een specifiek formaat moeten weergeven.

Stel je voor dat we een Java-type hebben dat de auteur van een bericht vertegenwoordigt:

public class Auteur {private String naam; privé String-e-mail; // constructeur, getters en setters}

Zonder enige moeite wordt dit geserialiseerd als een JSON-object met twee velden - naam en e-mail. We willen het echter serialiseren als een enkele string, waarbij de naam en het e-mailadres samen worden gecombineerd.

We doen dit door een standaardklasse te schrijven die een methode bevat die is geannoteerd met @Tomvangrieken:

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

Uiteraard moeten we ook de andere kant op gaan. We moeten onze string terug in onze Schrijver voorwerp. Dit wordt gedaan door een methode toe te voegen die is geannoteerd met @BuienRadarNL in plaats daarvan:

@FromJson openbare auteur fromJson (tekenreeksauteur) {Pattern pattern = Pattern.compile ("^ (. *) $"); Matcher matcher = pattern.matcher (auteur); return matcher.find ()? nieuwe auteur (matcher.group (1), matcher.group (2)): null; }

Eenmaal gedaan, moeten we hier echt gebruik van maken. We doen dit op het moment dat we onze Moshi door de adapter toe te voegen aan onze Moshi.Builder:

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

Nu kunnen we onmiddellijk beginnen met het converteren van deze objecten van en naar JSON, en de resultaten krijgen die we wilden:

Post post = nieuw bericht ("Mijn bericht", nieuwe auteur ("Baeldung", "[e-mail beschermd]"), "Dit is mijn bericht"); String json = jsonAdapter.toJson (bericht); // {"author": "Baeldung <[email protected]>", "text": "Dit is mijn bericht", "title": "Mijn bericht"} Post post = jsonAdapter.fromJson (json); // new Post ("My Post", nieuwe auteur ("Baeldung", "[email protected]"), "Dit is mijn bericht");

5.2. Complexe conversies

Deze conversies waren tussen Java Beans en JSON-primitieve typen. We kunnen ook converteren naar gestructureerde JSON - waardoor we in feite een Java-type naar een andere structuur kunnen converteren voor weergave in onze JSON.

Het kan bijvoorbeeld nodig zijn om een ​​datum / tijd-waarde weer te geven als drie verschillende waarden: de datum, de tijd en de tijdzone.

Met Moshi hoeven we alleen maar een Java-type te schrijven dat de gewenste uitvoer vertegenwoordigt en vervolgens onze @Tomvangrieken methode kan dit nieuwe Java-object retourneren, dat Moshi vervolgens naar JSON converteert met behulp van de standaardregels:

openbare klasse JsonDateTime {privé String-datum; privé String-tijd; private String tijdzone; // constructor, getters en setters} openbare klasse JsonDateTimeAdapter {@ToJson openbare JsonDateTime toJson (ZonedDateTime-invoer) {String date = input.toLocalDate (). toString (); String tijd = input.toLocalTime (). ToString (); Tekenreeks tijdzone = input.getZone (). ToString (); retourneer nieuwe JsonDateTime (datum, tijd, tijdzone); }}

Zoals we kunnen verwachten, doe je de andere kant op door een @BuienRadarNL methode die ons nieuwe JSON-gestructureerde type gebruikt en onze gewenste retourneert:

@FromJson openbare ZonedDateTime fromJson (JsonDateTime-invoer) {LocalDate date = LocalDate.parse (input.getDate ()); LocalTime-tijd = LocalTime.parse (input.getTime ()); ZoneId tijdzone = ZoneId.of (input.getTimezone ()); terugkeer ZonedDateTime.of (datum, tijd, tijdzone); }

We kunnen dit dan precies zoals hierboven gebruiken om onze ZonedDateTime in onze gestructureerde output en terug:

Moshi moshi = nieuwe Moshi.Builder () .add (nieuwe 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 [Europe / Londen]

5.3. Alternatieve type adapters

Soms willen we een alternatieve adapter gebruiken voor een enkel veld, in plaats van deze te baseren op het type veld.

We kunnen bijvoorbeeld een enkel geval hebben waarin we de datum en tijd moeten weergeven als milliseconden vanaf het tijdperk in plaats van als een ISO-8601-string.

Moshi laat ons dit doen door een speciaal geannoteerde annotatie te gebruiken die we vervolgens zowel op ons veld als op onze adapter kunnen toepassen:

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

Het belangrijkste onderdeel hiervan is de @Jonvaneerd annotatie, waarmee Moshi alle velden die hiermee zijn geannoteerd, aan de juiste Adaptermethoden kan koppelen.

Vervolgens moeten we een adapter schrijven. Zoals altijd hebben we beide een @BuienRadarNL en een @Tomvangrieken methode om te converteren tussen ons type en JSON:

openbare klasse EpochMillisAdapter {@ToJson openbare Long toJson (@EpochMillis Instant input) {return input.toEpochMilli (); } @FromJson @EpochMillis openbaar Instant fromJson (lange invoer) {retourneer Instant.ofEpochMilli (invoer); }}

Hier hebben we onze annotatie over de invoerparameter voor de @Tomvangrieken methode en op de geretourneerde waarde van de @BuienRadarNL methode.

Moshi kan nu deze adapter gebruiken of elk veld dat ook is geannoteerd @EpochMillis:

openbare klasse Post {privé String-titel; private String-auteur; @EpochMillis Direct geplaatst; // constructeur, getters en setters}

We zijn nu in staat om ons geannoteerde type naar JSON en indien nodig terug te converteren:

Moshi moshi = nieuwe Moshi.Builder () .add (nieuwe EpochMillisAdapter ()) .build (); JsonAdapter jsonAdapter = moshi.adapter (Post.class); String json = jsonAdapter.toJson (nieuw bericht ("Inleiding tot Moshi Json", "Baeldung", Instant.now ())); // {"author": "Baeldung", "posted": 1582095384793, "title": "Inleiding tot Moshi Json"} Post post = jsonAdapter.fromJson (json); // new Post ("Inleiding tot Moshi Json", "Baeldung", Instant.now ())

6. Geavanceerde JSON-verwerking

Nu we onze typen naar JSON en omgekeerd kunnen converteren, en we kunnen bepalen hoe deze conversie plaatsvindt. Er zijn echter enkele meer geavanceerde dingen die we af en toe moeten doen met onze verwerking, die Moshi gemakkelijk te realiseren maakt.

6.1. Hernoemen van JSON-velden

Af en toe hebben we onze JSON nodig om andere veldnamen te hebben dan onze Java-bonen. Dit kan zo simpel zijn als willen camelCase in Java en snake_case in JSON, of het kan zijn om het veld volledig te hernoemen zodat het overeenkomt met het gewenste schema.

We kunnen de @Json annotatie om een ​​nieuwe naam te geven aan elk veld in een bean die we beheren:

openbare klasse Post {privé String-titel; @Json (name = "authored_by") privé String-auteur; // constructeur, getters en setters}

Zodra we dit hebben gedaan, begrijpt Moshi onmiddellijk dat dit veld een andere naam heeft in de JSON:

Moshi moshi = nieuwe Moshi.Builder () .build (); JsonAdapter jsonAdapter = moshi.adapter (Post.class); Post post = nieuw bericht ("My Post", "Baeldung"); String json = jsonAdapter.toJson (bericht); // {"authored_by": "Baeldung", "title": "My Post"} Post post = jsonAdapter.fromJson (json); // nieuw bericht ("Mijn bericht", "Baeldung")

6.2. Voorbijgaande velden

In bepaalde gevallen kunnen we velden hebben die niet in de JSON moeten worden opgenomen. Moshi gebruikt de standaard voorbijgaand kwalificatie om aan te geven dat deze velden niet geserialiseerd of gedeserialiseerd mogen worden:

openbare statische klasse Post {private String-titel; privé voorbijgaande tekenreeks auteur; // constructeur, getters en setters}

We zullen dan zien dat dit veld volledig wordt genegeerd, zowel bij het serialiseren als bij het deserialiseren:

Moshi moshi = nieuwe Moshi.Builder () .build (); JsonAdapter jsonAdapter = moshi.adapter (Post.class); Post post = nieuw bericht ("My Post", "Baeldung"); String json = jsonAdapter.toJson (bericht); // {"title": "My Post"} Post post = jsonAdapter.fromJson (json); // nieuw bericht ("Mijn bericht", null) Bericht bericht = jsonAdapter.fromJson ("{\" auteur \ ": \" Baeldung \ ", \" titel \ ": \" Mijn bericht \ "}"); // nieuw bericht ("Mijn bericht", null)

6.3. Standaard waarden

Soms ontleden we JSON die niet voor elk veld in onze Java Bean waarden bevat. Dit is prima, en Moshi zal zijn best doen om het juiste te doen.

Moshi kan geen enkele vorm van argumentconstructor gebruiken bij het deserialiseren van onze JSON, maar het kan een constructor zonder args gebruiken als die aanwezig is.

Hierdoor kunnen we onze bean vooraf invullen voordat de JSON wordt geserialiseerd, waardoor alle vereiste standaardwaarden aan onze velden worden gegeven:

openbare klasse Post {privé String-titel; private String-auteur; private String geplaatst; openbare post () {gepost = Instant.now (). toString (); } // getters en setters}

Als onze geparseerde JSON de titel of schrijver velden dan zullen deze eindigen met de waarde nul. Als we de Geplaatst veld dan zal dit in plaats daarvan de huidige datum en tijd hebben:

Moshi moshi = nieuwe Moshi.Builder () .build (); JsonAdapter jsonAdapter = moshi.adapter (Post.class); String json = "{\" title \ ": \" Mijn bericht \ "}"; Post post = jsonAdapter.fromJson (json); // nieuw bericht ("Mijn bericht", null, "2020-02-19T07: 27: 01.141Z");

6.4. JSON-arrays parseren

Alles wat we tot nu toe hebben gedaan, gaat ervan uit dat we een enkel JSON-object serialiseren en deserialiseren in een enkele Java-bean. Dit is een veel voorkomend geval, maar het is niet het enige geval. Soms willen we ook werken met verzamelingen waarden, die in onze JSON als een array worden weergegeven.

Als de array in onze bonen is genest, is er niets te doen. Moshi zal gewoon werken. Als de volledige JSON een array is, moeten we meer werk doen om dit te bereiken, simpelweg vanwege enkele beperkingen in Java-generieke geneesmiddelen. We moeten onze JsonAdapter op een manier dat het weet dat het een generieke collectie deserialiseert, evenals wat de collectie is.

Moshi biedt wat hulp bij het construeren van een java.lang.reflect.Type die we kunnen leveren aan de JsonAdapter wanneer we het bouwen, zodat we deze aanvullende algemene informatie kunnen verstrekken:

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

Zodra dit is gebeurd, werkt onze adapter precies zoals verwacht, met inachtneming van deze nieuwe generieke grenzen:

String json = jsonAdapter.toJson (Arrays.asList ("Een", "Twee", "Drie")); // ["Een", "Twee", "Drie"] Lijstresultaat = jsonAdapter.fromJson (json); // Arrays.asList ("Een", "Twee", "Drie");

7. Samenvatting

We hebben gezien hoe de Moshi-bibliotheek het converteren van Java-klassen van en naar JSON heel gemakkelijk kan maken, en hoe flexibel het is. We kunnen deze bibliotheek overal gebruiken waar we moeten converteren tussen Java en JSON - of dat nu is om bestanden, databasekolommen of zelfs REST API's te laden en op te slaan. Waarom probeer je het niet uit?

Zoals gewoonlijk is de broncode voor dit artikel te vinden op GitHub.