1. Przegląd
Ten samouczek koncentruje się na zrozumieniu klasy Jackson ObjectMapper i sposobach serializacji obiektów Java do formatu JSON oraz deserializacji ciągu JSON na obiekty Java.
Aby dowiedzieć się więcej o bibliotece Jacksona, dobrym miejscem do rozpoczęcia jest samouczek Jacksona.
2. Zależności
Najpierw dodajmy następujące zależności do pom.xml :
com.fasterxml.jackson.core jackson-databind 2.11.1
Ta zależność spowoduje również przejściowe dodanie następujących bibliotek do ścieżki klas:
- adnotacje-jackson
- jackson-core
Zawsze używaj najnowszych wersji z centralnego repozytorium Maven dla jackson-databind .
3. Czytanie i pisanie przy użyciu ObjectMapper
Zacznijmy od podstawowych operacji odczytu i zapisu.
Prosty interfejs API readValue ObjectMapper jest dobrym punktem wejścia. Możemy go użyć do przeanalizowania lub deserializacji zawartości JSON w obiekt Java.
Również po stronie pisania możemy użyć API writeValue do serializacji dowolnego obiektu Java jako danych wyjściowych JSON.
W tym artykule będziemy używać następującej klasy Car z dwoma polami jako obiektem do serializacji lub deserializacji:
public class Car { private String color; private String type; // standard getters setters }
3.1. Obiekt Java na JSON
Zobaczmy pierwszy przykład serializacji obiektu Java do formatu JSON przy użyciu metody writeValue klasy ObjectMapper :
ObjectMapper objectMapper = new ObjectMapper(); Car car = new Car("yellow", "renault"); objectMapper.writeValue(new File("target/car.json"), car);
Efektem powyższego w pliku będzie:
{"color":"yellow","type":"renault"}
Sposoby writeValueAsString i writeValueAsBytes z ObjectMapper klasy generowania JSON z obiektu Java i powrót generowany JSON w postaci łańcucha lub tablicy bajtów:
String carAsString = objectMapper.writeValueAsString(car);
3.2. JSON na obiekt Java
Poniżej znajduje się prosty przykład konwersji ciągu JSON na obiekt Java przy użyciu klasy ObjectMapper :
String json = "{ \"color\" : \"Black\", \"type\" : \"BMW\" }"; Car car = objectMapper.readValue(json, Car.class);
Funkcja readValue () akceptuje również inne formy danych wejściowych, takie jak plik zawierający ciąg JSON:
Car car = objectMapper.readValue(new File("src/test/resources/json_car.json"), Car.class);
lub adres URL:
Car car = objectMapper.readValue(new URL("file:src/test/resources/json_car.json"), Car.class);
3.3. JSON do Jackson JsonNode
Alternatywnie JSON może zostać przeanalizowany do obiektu JsonNode i użyty do pobrania danych z określonego węzła:
String json = "{ \"color\" : \"Black\", \"type\" : \"FIAT\" }"; JsonNode jsonNode = objectMapper.readTree(json); String color = jsonNode.get("color").asText(); // Output: color -> Black
3.4. Tworzenie listy Java z ciągu JSON Array
Możemy przeanalizować JSON w postaci tablicy do listy obiektów Java za pomocą TypeReference :
String jsonCarArray = "[{ \"color\" : \"Black\", \"type\" : \"BMW\" }, { \"color\" : \"Red\", \"type\" : \"FIAT\" }]"; List listCar = objectMapper.readValue(jsonCarArray, new TypeReference
(){});
3.5. Tworzenie mapy Java z ciągu JSON
Podobnie możemy przeanalizować JSON w mapę Java :
String json = "{ \"color\" : \"Black\", \"type\" : \"BMW\" }"; Map map = objectMapper.readValue(json, new TypeReference
4. Funkcje zaawansowane
Jedną z największych zalet biblioteki Jacksona jest wysoce konfigurowalny proces serializacji i deserializacji.
W tej sekcji omówimy niektóre zaawansowane funkcje, w których wejściowa lub wyjściowa odpowiedź JSON może różnić się od obiektu, który generuje lub zużywa odpowiedź.
4.1. Konfigurowanie funkcji serializacji lub deserializacji
Podczas konwersji obiektów JSON na klasy Java, w przypadku, gdy ciąg JSON ma jakieś nowe pola, domyślny proces zakończy się wyjątkiem:
String jsonString = "{ \"color\" : \"Black\", \"type\" : \"Fiat\", \"year\" : \"1970\" }";
Ciąg JSON w powyższym przykładzie w domyślnym procesie analizy do obiektu Java dla klasy samochodu spowoduje wyjątek UnrecognizedPropertyException .
Za pomocą metody konfiguracji możemy rozszerzyć domyślny proces, aby zignorować nowe pola :
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();
Jeszcze inna opcja jest oparta na FAIL_ON_NULL_FOR_PRIMITIVES , która określa, czy dozwolone są wartości null dla wartości pierwotnych:
objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false);
Podobnie FAIL_ON_NUMBERS_FOR_ENUM kontroluje, czy wartości wyliczenia mogą być serializowane / deserializowane jako liczby:
objectMapper.configure(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS, false);
Pełną listę funkcji serializacji i deserializacji można znaleźć w oficjalnej witrynie.
4.2. Tworzenie niestandardowego serializatora lub deserializatora
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
Inną małą, ale użyteczną funkcją dostępną za pośrednictwem klasy DeserializationFeature jest możliwość wygenerowania żądanego typu kolekcji z odpowiedzi JSON Array.
Na przykład możemy wygenerować wynik jako tablicę:
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
Lub jako lista :
String jsonCarArray = "[{ \"color\" : \"Black\", \"type\" : \"BMW\" }, { \"color\" : \"Red\", \"type\" : \"FIAT\" }]"; ObjectMapper objectMapper = new ObjectMapper(); List listCar = objectMapper.readValue(jsonCarArray, new TypeReference
(){}); // print cars
Więcej informacji na temat obsługi kolekcji z Jacksonem można znaleźć tutaj.
5. Wniosek
Jackson jest solidną i dojrzałą biblioteką serializacji / deserializacji JSON dla języka Java. ObjectMapper API zapewnia prosty sposób analizowania i generować obiekty JSON odpowiedzi z dużą elastycznością. W tym artykule omówiono główne funkcje, które sprawiają, że biblioteka jest tak popularna.
Kod źródłowy dołączony do artykułu można znaleźć na GitHub.