Java >> Java opplæring >  >> Java

Introduksjon til Jackson ObjectMapper

I dette eksemplet vil vi lære om ObjectMapper-klassen fra Jackson og dens evner til å serialisere POJOs (Plain Old Java Objects) til JSON-strenger og deserialisere JSON-strenger tilbake til Java-objekter, med noen eksempler.

1. Hvor skal jeg begynne?

Det første trinnet for å bli skitne med ObjectMapper og Jackson databinding er å få bibliotekene og legge dem til klassebanen. Den enkleste måten ville være å legge til følgende avhengighet til listen over prosjektavhengigheter i pom.xml til maven-prosjektet ditt.

avhengigheter
<dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-databind</artifactId>
   <version>2.11.0</version>
</dependency>

Jeg bruker versjon 2.11.0 som tilfeldigvis er den siste da jeg skrev denne artikkelen. For andre versjoner, sjekk Maven Central Repository her.

Ovennevnte avhengighet vil legge til følgende biblioteker til klassebanen:

  • jackson-databind-2.11.0.jar
  • jackson-annotations-2.11.0.jar
  • jackson-core-2.11.0.jar

Hvis du prøver å laste ned jackson-databind-krukken manuelt og legge den til Java-byggebanen, sørg for at du laster ned og legger til de to andre bibliotekene også siden data-bind-biblioteket trenger de to andre under kjøring.

2. ObjectMapper-klassen

Å serialisere en POJO til en JSON-streng eller deserialisere en JSON-streng til et objekt krever en forekomst av ObectMapper-klassen som gir fire konstruktører som kan brukes til å lage en forekomst.

I dette eksemplet skal vi lage en forekomst ved å bruke standardkonstruktøren og utføre alle serialiserings- og deserialiseringsoperasjoner.

Opprette ObjectMapper-forekomsten
   ObjectMapper objectMapper = new ObjectMapper();

2.1. Serialiser en POJO til JSON-streng

Gjennom denne artikkelen vil vi bruke følgende land klasse for alle serialiserings- og deserialiseringsoperasjoner.

Country.java
public class Country {

	private String name;
	private long population;
	private int numberOfProvinces;
	private boolean developed;

	public Country(String name, long population, int numberOfProvinces,
						boolean developed) {
		this.name = name;
		this.population = population;
		this.numberOfProvinces = numberOfProvinces;
		this.developed = developed;
	}

   // getters and setters
}

Metoden writeValueAsString av ObjectMapper-klassen tar et objekt som et argument og returnerer den genererte JSON som en streng.POJO To String

		Country country = new Country("India", 135260000000L, 29, true);
		String countryAsString = objectMapper.writeValueAsString(country);
		System.out.println(countryAsString);
Utdata:
{"name":"India","population":135260000000,"numberOfProvinces":29,"developed":true}

writeValue metoden tar to argumenter, et filobjekt der JSON skal skrives til, og et kildeobjekt som skal serialiseres. Ved å kjøre denne metoden skrives den genererte JSON-en til den oppgitte filen.Serialiser POJO som JSON til en fil

		objectMapper.writeValue(new File("target/country.json"), country);

		byte[] countryAsBytes = objectMapper.writeValueAsBytes(country);

Tilsvarende er metoden writeValueAsBytes serialiserer et Java-objekt som en byte-array.

ObjectMapper-klassen gir også overbelastede metoder writeValue som tar et argument av typen java.io.OutputStream og java.io.Writer. Denne verdien av dette argumentet brukes til å serialisere java-objektet som sendes som det andre argumentet.

2.2. JSON-streng til et Java-objekt

leseverdien metoden til ObjectMapper-klassen konverterer en JSON-streng til et Java-objekt som vist i eksemplet nedenfor. Det andre argumentet til readValue-metoden er av typen Class som er målklassen som JSON må deserialiseres til.JSON-streng til Java-objekt

		String countryStr = "{\"name\":\"India\",\"population\":135260000000,"
				+ "\"numberOfProvinces\":29,\"developed\":true}";
		Country countryFromString = objectMapper.readValue(countryStr, Country.class);

Målklassen må gi en standardkonstruktør for eksempel opprettelse, og hvis den ikke er tilgjengelig, vil deserialiseringsprosessen mislykkes med feilen:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException :Kan ikke konstruere forekomst av `com.adee.samples.objectmapper.model.Country` (ingen skapere, som standardkonstruktør, finnes):kan ikke deserialisere fra objektverdi (ingen delegat- eller eiendomsbasert skaper) JSON-streng i en fil til objekt

		Country countryFromFile = objectMapper.readValue(
				new File("target/country.json"), Country.class);
		System.out.println("jsonInFileToObject : " + countryFromFile + "\n");

Koden ovenfor viser en overstyrt metode readValue som tar et filobjekt som et argument. JSON-strengen i denne filen leses og deserialiseres til et Java-objekt.

Følgende er noen andre varianter av den overbelastede readValue metode som bruker henholdsvis Reader, InputStream, byte[] og URL for å deserialisere JSON til et Java-objekt.

  • readValue(Reader src, Class valueType)
  • readValue(InputStream src, Class valueType)
  • readValue(byte[] src, Class valueType)
  • readValue(URL src, Class valueType)

2.3. JSON-streng til java.util.Map

En JSON-streng kan analyseres og transformeres til et java.util.Map ved å bruke en TypeReference på følgende måte.JSON-streng til java.util.Map

		String countryStr = "{\"name\":\"India\",\"population\":135260000000,"
				+ "\"numberOfProvinces\":29,\"developed\":true}";
		Map jsonStringToMap = objectMapper.readValue(countryStr,
				new TypeReference<Map>() {
				});
		System.out.println("Country as a Map : " + jsonStringToMap);
Utgang av koden ovenfor
   
Country as a Map : {name=India, population=135260000000,
numberOfProvinces=29, developed=true}

2.4. JSON Array til java.util.List

På samme måte kan et JSON-objekt som inneholder en matrise deserialiseres til et Java-objekt av typen java.util.List . Se eksempelet nedenfor som viser dette.JSON Array To List

		String countryArrayStr = "[{\"name\":\"India\",\"population\":135260000000,"
				+ "\"numberOfProvinces\":29,\"developed\":true},{\"name\":\"SomeCountry\","
				+ "\"population\":123456789000,\"numberOfProvinces\":45,"
				+ "\"developed\":true}]";
		List countryArrayAsList = objectMapper.readValue
				(countryArrayStr, new TypeReference<List>() {
		});
		System.out.println(countryArrayAsList);
Utgang av koden ovenfor
		[Country [name=India, population=135260000000, numberOfProvinces=29,
		developed=true], Country [name=SomeCountry, population=123456789000,
		numberOfProvinces=45, developed=true]]

2,5. JSON-streng til JsonNode

En JSON kan også analyseres til en com.fasterxml.jackson.databind.JsonNode ved å påkalle readTree metoden til ObjectMapper-klassen og sender kilden JSON som et argument. JsonNode kan videre brukes til å hente verdiene til individuelle felt med ønsket type etter behov.JSON String To JsonNode

		JsonNode jsonNode = objectMapper.readTree(countryStr);
		String name = jsonNode.get("name").asText();
		Long population = jsonNode.get("population").asLong();
		Integer provinces = jsonNode.get("numberOfProvinces").asInt();
		boolean isDeveloped = jsonNode.get("developed").asBoolean();

2.6. Opprette en JSON-struktur

ObjectMapper-klassen gir også metoder for å lage ObjectNode og ArrayNode og generere en JSON-struktur som en kombinasjon av JsonNode-objekter. Følgende kodebit demonstrerer dette.Opprette en JsonNode-struktur

		ObjectNode root = objectMapper.createObjectNode();
		root.put("asText", "SampleString");
		root.put("asBoolean", false);
		ArrayNode array = root.putArray("asArray");
		Country country = new Country("India", 135260000000L, 29, true);
		Country countryFromFile = objectMapper.readValue(
				new File("target/random.json"), Country.class);
		array.addPOJO(country);
		array.addPOJO(countryFromFile);
		System.out.println(objectMapper.writerWithDefaultPrettyPrinter()
				.writeValueAsString(root));

Koden ovenfor bruker PrettyPrinter og genererer følgende formaterte utdata, pent skrevet ut som du kan se nedenfor.

{
  "asText" : "SampleString",
  "asBoolean" : false,
  "asArray" : [ {
    "name" : "India",
    "population" : 135260000000,
    "numberOfProvinces" : 29,
    "developed" : true
  }, {
    "name" : "Dummy",
    "population" : 1987634509,
    "numberOfProvinces" : 15,
    "developed" : true
  } ]
}

3. Konfigurere ObjectMapper

Det kan være scenarier der inndata-JSON er forskjellig fra eller inkompatibel med POJO for standard deserialiseringsprosessen som brukes av Jackson API. Noen av dem er som følger:

  • JSON-strengen har felt som ikke er tilgjengelige i den tilsvarende POJO.
  • JSON-streng har nullverdier for felt med primitive typer.

La oss se hva som skjer når en slik JSON sendes til deserialisering og hvordan du kan fikse eventuelle feil som oppstår i disse tilfellene.

3.1. JSON-felter utilgjengelige i POJO

Hvis JSON-strengen har noen felt som er ukjente for POJO, vil en UnrecognizedPropertyException kastes av standard deserialiseringsprosess.JSON-felt er ikke tilgjengelig i POJO

		String countryStrUnknownField = "{\"name\":\"India\",\"population\":135260000000,"
				+ "\"numberOfProvinces\":29,\"developed\":true, "
				+ "\"extraField\":\"some-value\"}";
		Country countryUnknownField = objectMapper.readValue(
					countryStrUnknownField, Country.class);

Utførelse av kodefeilene ovenfor med meldingen:

Ukjent feltet «extraField ” (klassen com.adee.samples.objectmapper.model.Country), ikke merket som ignorerbar.

konfigurer metoden til ObjectMapper-klassen lar oss ignorere alle felt i JSON-strengen som er ukjent for POJO ved å bruke deserialiseringsfunksjonen FAIL_ON_UNKNOWN_PROPERTIES . Følgende kode demonstrerer dette.

		objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
		Country countryUnknownField = objectMapper.readValue
				(countryStrUnknownField, Country.class);
		System.out.println(countryUnknownField);
		// prints; Field extraField is ignored
		Country [name=India, population=135260000000, numberOfProvinces=29, developed=true]

3.2. NULL-verdier for primitive typer

En annen deserialiseringsfunksjon FAIL_ON_NULL_FOR_PRIMITIVES definerer om de primitive typene har lov til å holde null verdier. En verdi på true for denne funksjonen vil feile ut en deserialiseringsoperasjon hvis input-JSON har nullverdier for primitive typer.NULL-verdier for primitive typer

		objectMapper.configure(DeserializationFeature
				.FAIL_ON_NULL_FOR_PRIMITIVES, true);
		String countryStrPrimitiveNull = "{\"name\":\"India\","
				+ "\"population\":135260000000,\"numberOfProvinces\""
				+ ":null,\"developed\":true}";
		countryPrimitiveNull = objectMapper.readValue
					(countryStrPrimitiveNull, Country.class);

Koden ovenfor mislykkes når den kjøres med en MismatchedInputException :Kan ikke tilordne «null» til typen int. Standardverdien for funksjonen FAIL_ON_NULL_FOR_PRIMITIVES er falsk.

3.4. Andre deserialiseringsfunksjoner

  • FAIL_ON_NUMBERS_FOR_ENUMS funksjonen brukes til å kontrollere om tall er tillatt som enum-verdier for serialisering/deserialisering.
  • FAIL_ON_IGNORED_PROPERTIES funksjonen, hvis den er aktivert, kaster en JsonMappingException når en egenskap som er eksplisitt merket som ignorerbar i POJO, vises i JSON-strengen.
  • FAIL_ON_READING_DUP_TREE_KEY funksjonen, hvis den er aktivert, kaster en JsonMappingException hvis en duplikatnøkkel oppdages når JSON-innhold transformeres til et tre (JsonNode).

4. Arbeide med datoformater

Info.java
public class Info {

	private Country country;
	private Date now;

	public Info(Country country, Date now) {
		this.country = country;
		this.now = now;
	}
        
        // getters and setters
}

For å demonstrere serialiserings-/deserialiseringsoperasjoner for objekter med datoer, vil vi vurdere POJO Info ovenfor som omslutter Country og inneholder en "now"-egenskap som er av typen Dato. Standard serialisering av et Date-objekt resulterer i epoke (antall millisekunder siden 1. januar 1970, UTC), som er et tall og er vanskelig å lese og forstå som vi kan se nedenfor.

objectWithDateToJsonString {"country":{"name":"India","population":135260000000,
"numberOfProvinces":29,"developed":true},"now":1591758641344}

ObjectMapper-klassen gir en metode setDateFormat som tar en forekomst av SimpleDateFormat som et argument. Serialiseringsoperasjonen etter denne konfigurasjonen genererer en dato i et format som kan leses av mennesker. Se eksemplet nedenfor.ObjectMapper Set DateFormat Serialization

		DateFormat df = new SimpleDateFormat("EEE MMM dd HH:mm:ssZ yyyy");
		objectMapper.setDateFormat(df);
		Info info = new Info(country, new Date());
		String objWithDateAsJsonString = objectMapper.writeValueAsString(info);
		System.out.println(objWithDateAsJsonString);

		// Prints {"country":{"name":"India","population":135260000000,
		//"numberOfProvinces":29,"developed":true},
		//"now":"Wed Jun 10 08:50:42+0530 2020"}

Tilsvarende, for å deserialisere en dato i et bestemt format, må et SimpleDateFormat-objekt opprettes og settes til ObjectMapper før deserialisering, ellers en InvalidFormatException vil bli kastet under kjøring.ObjectMapper Set DateFormat Deseialization

		DateFormat df = new SimpleDateFormat("EEE MMM dd HH:mm:ssZ yyyy");
		objectMapper.setDateFormat(df);
		String infoAsString = "{\"country\":{\"name\":\"India\","
				+ "\"population\":135260000000,\"numberOfProvinces\":29,"
				+ "\"developed\":true},\"now\":\"Tue Jan 01 01:01:01+0230 2020\"}";
		Info info = objectMapper.readValue(infoAsString, Info.class);
		System.out.println("jsonStringWithDateToObject " + info.getNow() + "\n");
		// Prints Wed Jan 01 04:01:01 IST 2020

5. Registrering av tilpassede serialiserere og deserialiserere

ObjectMapper-klassen gir funksjonalitet for å registrere tilpassede serializers og deserializers. Tilpasning er nyttig i scenarier når kilde- eller mål-JSON-strukturen er forskjellig fra Java POJO som den er deserialisert til eller er serialisert til.

5.1. Custom Serializer

CustomCountrySerializer

Nedenfor er en implementering av en tilpasset serializer som utvider basisklassen StdSerializer. Serialiseringslogikken skal skrives i den overstyrte serialiseringsmetoden.

		class CustomCountrySerializer extends StdSerializer {

			private static final long serialVersionUID = 1L;

			public CustomCountrySerializer() {
				this(null);
			}

			public CustomCountrySerializer(Class clazz) {
				super(clazz);
			}

			@Override
			public void serialize(Country country, JsonGenerator jsonGenerator,
					SerializerProvider serializer)
					throws IOException {
				jsonGenerator.writeStartObject();
				jsonGenerator.writeStringField("country_name_only_field", 
						country.getName());
				jsonGenerator.writeEndObject();
			}
		}

Den tilpassede serialiseringen kan påkalles ved å registrere den med ObjectMapper og bruke de vanlige metodene for serialisering. Dette er demonstrert i eksemplet nedenfor.Bruke The Custom Serializer

		ObjectMapper oMapper = new ObjectMapper();
		SimpleModule simpleModule = new SimpleModule("CustomCountrySerializer", new Version(1, 0, 0, null, null, null));
		simpleModule.addSerializer(Country.class, new CustomCountrySerializer());
		oMapper.registerModule(simpleModule);
		String countryJsonFromCustomSerializer = oMapper.writeValueAsString(country);
		System.out.println("demoCustomSerializer : " + countryJsonFromCustomSerializer);

Etter å ha utført koden ovenfor, vil følgende JSON-streng bli skrevet ut.

demoCustomSerializer : {"country_name_only_field":"India"}

5.2. Customer Deserializer

CustomCountryDeserializer

På samme måte, nedenfor er et eksempel på å lage en egendefinert JSON-deserializer . Logikken for deserialisering bør skrives i den overstyrte deserialiseringsmetoden.

		class CustomCountryDeserializer extends StdDeserializer {

			private static final long serialVersionUID = 1L;

			public CustomCountryDeserializer() {
				this(null);
			}

			public CustomCountryDeserializer(Class clazz) {
				super(clazz);
			}

			@Override
			public Country deserialize(JsonParser jsonParser,
					DeserializationContext deserializationContext)
					throws IOException {
				Country country = new Country();
				JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser);
				JsonNode customNameNode = jsonNode.get("customObjectName");
				String name = customNameNode.asText();
				country.setName(name);
				country.setNumberOfProvinces(Integer.MAX_VALUE);
				country.setPopulation(Long.MAX_VALUE);
				return country;
			}
		}

Deserialiseringsmetoden i koden ovenfor forventer en egenskap customObjectName i input-JSON-strengen som leses og settes som navn i Country-objektet.

Akkurat som den tilpassede serializeren, bør den tilpassede deserializeren først registreres med ObjectMapper etterfulgt av å påkalle de vanlige metodene for deserialisering.Bruke den tilpassede deserializeren

		String incompleteCountryJsonStr = "{\"customObjectName\":\"India\"}";
		ObjectMapper oMapper = new ObjectMapper();
		SimpleModule simpleModule = new SimpleModule("CustomCountrySerializer", new Version(1, 0, 0, null, null, null));
		simpleModule.addDeserializer(Country.class, new CustomCountryDeserializer());
		oMapper.registerModule(simpleModule);
		Country country = oMapper.readValue(incompleteCountryJsonStr, Country.class);
		System.out.println("demoCustomDeSerializer : " + country);

Etter vellykket deserialisering vil println-setningen sende ut følgende

		demoCustomDeSerializer : Country [name=India, population=9223372036854775807,
				numberOfProvinces=2147483647, developed=false]

6. Sammendrag

I dette eksemplet introduserte vi ObjectMapper-klassen til Jackson-biblioteket for JSON-serialiserings-/deserialiseringsoperasjoner. Vi så noen av egenskapene til ObjectMapper og implementerte også våre tilpassede serializers og deserializers.

7. Last ned kildekoden

jackson json JsonParser ObjectMapper serialisering
Java Tag