Introducere în Jackson ObjectMapper

1. Prezentare generală

Acest tutorial se concentrează pe înțelegerea clasei Jackson ObjectMapper și modul de serializare a obiectelor Java în JSON și deserializarea șirului JSON în obiecte Java.

Pentru a înțelege mai multe despre biblioteca Jackson în general, Tutorialul Jackson este un loc bun pentru a începe.

2. Dependențe

Să adăugăm mai întâi următoarele dependențe la pom.xml :

 com.fasterxml.jackson.core jackson-databind 2.11.1  

Această dependență va adăuga, de asemenea, tranzitiv următoarele biblioteci la classpath:

  1. adnotări jackson
  2. jackson-core

Utilizați întotdeauna cele mai recente versiuni din depozitul central Maven pentru jackson-databind .

3. Citirea și scrierea folosind ObjectMapper

Să începem cu operațiile de citire și scriere de bază.

API-ul simplu readValue al ObjectMapper este un bun punct de intrare. Îl putem folosi pentru a analiza sau deserializa conținutul JSON într-un obiect Java.

De asemenea, pe partea de scriere, putem utiliza API-ul writeValue pentru a serializa orice obiect Java ca ieșire JSON.

Vom folosi următoarea clasă de mașină cu două câmpuri ca obiect pentru a serializa sau deserializa în tot acest articol:

public class Car { private String color; private String type; // standard getters setters }

3.1. Obiect Java către JSON

Să vedem un prim exemplu de serializarea unui obiect Java în JSON folosind writeValue metoda de ObjectMapper clasa:

ObjectMapper objectMapper = new ObjectMapper(); Car car = new Car("yellow", "renault"); objectMapper.writeValue(new File("target/car.json"), car); 

Ieșirea celor de mai sus în fișier va fi:

{"color":"yellow","type":"renault"} 

Metodele writeValueAsString și writeValueAsBytes din clasa ObjectMapper generează un JSON dintr-un obiect Java și returnează JSON generat ca șir sau ca matrice de octeți:

String carAsString = objectMapper.writeValueAsString(car); 

3.2. JSON către obiectul Java

Mai jos este un exemplu simplu de conversie a unui șir JSON într-un obiect Java utilizând clasa ObjectMapper :

String json = "{ \"color\" : \"Black\", \"type\" : \"BMW\" }"; Car car = objectMapper.readValue(json, Car.class); 

Funcția readValue () acceptă și alte forme de intrare, cum ar fi un fișier care conține șir JSON:

Car car = objectMapper.readValue(new File("src/test/resources/json_car.json"), Car.class);

sau o adresă URL:

Car car = objectMapper.readValue(new URL("file:src/test/resources/json_car.json"), Car.class);

3.3. JSON către Jackson JsonNode

Alternativ, un JSON poate fi analizat într-un obiect JsonNode și utilizat pentru a extrage date dintr-un anumit nod:

String json = "{ \"color\" : \"Black\", \"type\" : \"FIAT\" }"; JsonNode jsonNode = objectMapper.readTree(json); String color = jsonNode.get("color").asText(); // Output: color -> Black 

3.4. Crearea unei liste Java dintr-un șir de matrice JSON

Putem analiza un JSON sub forma unui tablou într-o listă de obiecte Java folosind un TypeReference :

String jsonCarArray = "[{ \"color\" : \"Black\", \"type\" : \"BMW\" }, { \"color\" : \"Red\", \"type\" : \"FIAT\" }]"; List listCar = objectMapper.readValue(jsonCarArray, new TypeReference
    
     (){}); 
    

3.5. Crearea hărții Java din șirul JSON

În mod similar, putem analiza un JSON într-o hartă Java :

String json = "{ \"color\" : \"Black\", \"type\" : \"BMW\" }"; Map map = objectMapper.readValue(json, new TypeReference(){}); 

4. Funcții avansate

Unul dintre cele mai mari puncte forte ale bibliotecii Jackson este procesul de serializare și deserializare extrem de personalizabil.

În această secțiune, vom trece prin câteva caracteristici avansate în care răspunsul de intrare sau de ieșire JSON poate fi diferit de obiectul care generează sau consumă răspunsul.

4.1. Configurarea caracteristicii de serializare sau deserializare

În timp ce convertiți obiecte JSON în clase Java, în cazul în care șirul JSON are câmpuri noi, procesul implicit va avea ca rezultat o excepție:

String jsonString = "{ \"color\" : \"Black\", \"type\" : \"Fiat\", \"year\" : \"1970\" }"; 

Șirul JSON din exemplul de mai sus în procesul de analiză implicit la obiectul Java pentru Class Car va avea ca rezultat excepția UnrecognizedPropertyException .

Prin metoda de configurare , putem extinde procesul implicit pentru a ignora noile câmpuri :

objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); Car car = objectMapper.readValue(jsonString, Car.class); JsonNode jsonNodeRoot = objectMapper.readTree(jsonString); JsonNode jsonNodeYear = jsonNodeRoot.get("year"); String year = jsonNodeYear.asText(); 

O altă opțiune se bazează pe FAIL_ON_NULL_FOR_PRIMITIVES , care definește dacă sunt permise valorile nule pentru valorile primitive:

objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false); 

În mod similar, FAIL_ON_NUMBERS_FOR_ENUM controlează dacă valorile enum pot fi serializate / deserializate ca numere:

objectMapper.configure(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS, false);

Puteți găsi lista cuprinzătoare a caracteristicilor de serializare și deserializare pe site-ul oficial.

4.2. Crearea serializatorului personalizat sau a deserializatorului

Another essential feature of the ObjectMapper class is the ability to register a custom serializer and deserializer.

Custom serializers and deserializers are very useful in situations where the input or the output JSON response is different in structure than the Java class into which it must be serialized or deserialized.

Below is an example of a custom JSON serializer:

public class CustomCarSerializer extends StdSerializer { public CustomCarSerializer() { this(null); } public CustomCarSerializer(Class t) { super(t); } @Override public void serialize( Car car, JsonGenerator jsonGenerator, SerializerProvider serializer) { jsonGenerator.writeStartObject(); jsonGenerator.writeStringField("car_brand", car.getType()); jsonGenerator.writeEndObject(); } } 

This custom serializer can be invoked like this:

ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule("CustomCarSerializer", new Version(1, 0, 0, null, null, null)); module.addSerializer(Car.class, new CustomCarSerializer()); mapper.registerModule(module); Car car = new Car("yellow", "renault"); String carJson = mapper.writeValueAsString(car); 

Here's what the Car looks like (as JSON output) on the client side:

var carJson = {"car_brand":"renault"} 

And here's an example of a custom JSON deserializer:

public class CustomCarDeserializer extends StdDeserializer { public CustomCarDeserializer() { this(null); } public CustomCarDeserializer(Class vc) { super(vc); } @Override public Car deserialize(JsonParser parser, DeserializationContext deserializer) { Car car = new Car(); ObjectCodec codec = parser.getCodec(); JsonNode node = codec.readTree(parser); // try catch block JsonNode colorNode = node.get("color"); String color = colorNode.asText(); car.setColor(color); return car; } } 

This custom deserializer can be invoked in this way:

String json = "{ \"color\" : \"Black\", \"type\" : \"BMW\" }"; ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule("CustomCarDeserializer", new Version(1, 0, 0, null, null, null)); module.addDeserializer(Car.class, new CustomCarDeserializer()); mapper.registerModule(module); Car car = mapper.readValue(json, Car.class); 

4.3. Handling Date Formats

The default serialization of java.util.Date produces a number, i.e., epoch timestamp (number of milliseconds since January 1, 1970, UTC). But this is not very human readable and requires further conversion to be displayed in a human-readable format.

Let's wrap the Car instance we used so far inside the Request class with the datePurchased property:

public class Request { private Car car; private Date datePurchased; // standard getters setters } 

To control the String format of a date and set it to, e.g., yyyy-MM-dd HH:mm a z, consider the following snippet:

ObjectMapper objectMapper = new ObjectMapper(); DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm a z"); objectMapper.setDateFormat(df); String carAsString = objectMapper.writeValueAsString(request); // output: {"car":{"color":"yellow","type":"renault"},"datePurchased":"2016-07-03 11:43 AM CEST"} 

To learn more about serializing dates with Jackson, read our more in-depth write-up.

4.4. Handling Collections

O altă caracteristică mică, dar utilă disponibilă prin clasa DeserializationFeature este abilitatea de a genera tipul de colecție pe care îl dorim dintr-un răspuns JSON Array.

De exemplu, putem genera rezultatul ca o matrice:

String jsonCarArray = "[{ \"color\" : \"Black\", \"type\" : \"BMW\" }, { \"color\" : \"Red\", \"type\" : \"FIAT\" }]"; ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, true); Car[] cars = objectMapper.readValue(jsonCarArray, Car[].class); // print cars

Sau ca listă :

String jsonCarArray = "[{ \"color\" : \"Black\", \"type\" : \"BMW\" }, { \"color\" : \"Red\", \"type\" : \"FIAT\" }]"; ObjectMapper objectMapper = new ObjectMapper(); List listCar = objectMapper.readValue(jsonCarArray, new TypeReference
    
     (){}); // print cars
    

Mai multe informații despre manipularea colecțiilor cu Jackson sunt disponibile aici.

5. Concluzie

Jackson este o bibliotecă solidă și matură de serializare / deserializare JSON pentru Java. ObjectMapper API oferă o modalitate simplă de a analiza și de a genera obiecte de răspuns JSON , cu o mulțime de flexibilitate. Acest articol a discutat despre principalele caracteristici care fac biblioteca atât de populară.

Codul sursă care însoțește articolul poate fi găsit pe GitHub.