1. Общ преглед
Този урок се фокусира върху разбирането на класа Jackson ObjectMapper и как да сериализирате Java обекти в JSON и десериализирате JSON низ в Java обекти.
За да разберете повече за библиотеката на Джаксън като цяло, Урокът за Джаксън е добро начало.
2. Зависимости
Нека първо добавим следните зависимости към pom.xml :
com.fasterxml.jackson.core jackson-databind 2.11.1
Тази зависимост също ще добави преходно следните библиотеки към пътя на класа:
- анотации на jackson
- jackson-core
Винаги използвайте най-новите версии от централното хранилище на Maven за jackson-databind .
3. Четене и писане с помощта на ObjectMapper
Нека започнем с основните операции за четене и запис.
Простият API на readValue на ObjectMapper е добра входна точка. Можем да го използваме, за да анализираме или десериализираме JSON съдържание в Java обект.
Също така, от страна на записването, можем да използваме API на writeValue, за да сериализираме всеки Java обект като JSON изход.
Ще използваме следния клас Car с две полета като обект, за да сериализираме или десериализираме в цялата статия:
public class Car { private String color; private String type; // standard getters setters }
3.1. Java обект на JSON
Нека видим първи пример за сериализиране на Java обект в JSON с помощта на метода writeValue на класа ObjectMapper :
ObjectMapper objectMapper = new ObjectMapper(); Car car = new Car("yellow", "renault"); objectMapper.writeValue(new File("target/car.json"), car);
Резултатът от горното във файла ще бъде:
{"color":"yellow","type":"renault"}
Методите writeValueAsString и writeValueAsBytes от клас ObjectMapper генерират JSON от Java обект и връщат генерирания JSON като низ или като байтов масив:
String carAsString = objectMapper.writeValueAsString(car);
3.2. JSON към Java обект
По-долу е прост пример за преобразуване на JSON String в Java обект с помощта на ObjectMapper клас:
String json = "{ \"color\" : \"Black\", \"type\" : \"BMW\" }"; Car car = objectMapper.readValue(json, Car.class);
Функцията readValue () приема и други форми на въвеждане, като файл, съдържащ JSON низ:
Car car = objectMapper.readValue(new File("src/test/resources/json_car.json"), Car.class);
или URL адрес:
Car car = objectMapper.readValue(new URL("file:src/test/resources/json_car.json"), Car.class);
3.3. JSON към Джаксън JsonNode
Алтернативно, JSON може да бъде анализиран в JsonNode обект и използван за извличане на данни от определен възел:
String json = "{ \"color\" : \"Black\", \"type\" : \"FIAT\" }"; JsonNode jsonNode = objectMapper.readTree(json); String color = jsonNode.get("color").asText(); // Output: color -> Black
3.4. Създаване на списък на Java от JSON Array String
Можем да анализираме JSON под формата на масив в списък с обекти на Java, използвайки TypeReference :
String jsonCarArray = "[{ \"color\" : \"Black\", \"type\" : \"BMW\" }, { \"color\" : \"Red\", \"type\" : \"FIAT\" }]"; List listCar = objectMapper.readValue(jsonCarArray, new TypeReference
(){});
3.5. Създаване на Java карта от JSON String
По същия начин можем да анализираме JSON в Java Map :
String json = "{ \"color\" : \"Black\", \"type\" : \"BMW\" }"; Map map = objectMapper.readValue(json, new TypeReference
4. Разширени функции
Една от най-големите силни страни на библиотеката на Джаксън е изключително адаптивният процес на сериализация и десериализация.
В този раздел ще разгледаме някои разширени функции, при които входният или изходният JSON отговор може да се различава от обекта, който генерира или консумира отговора.
4.1. Конфигуриране на функцията за сериализация или десериализация
Докато конвертирате JSON обекти в Java класове, в случай че JSON низът има някои нови полета, процесът по подразбиране ще доведе до изключение:
String jsonString = "{ \"color\" : \"Black\", \"type\" : \"Fiat\", \"year\" : \"1970\" }";
The JSON string in the above example in the default parsing process to the Java object for the Class Car will result in the UnrecognizedPropertyException exception.
Through the configure method, we can extend the default process to ignore the new fields:
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();
Yet another option is based on the FAIL_ON_NULL_FOR_PRIMITIVES, which defines if the null values for primitive values are allowed:
objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false);
Similarly, FAIL_ON_NUMBERS_FOR_ENUM controls if enum values are allowed to be serialized/deserialized as numbers:
objectMapper.configure(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS, false);
You can find the comprehensive list of serialization and deserialization features on the official site.
4.2. Creating Custom Serializer or Deserializer
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
Друга малка, но полезна функция, достъпна чрез класа DeserializationFeature, е възможността да се генерира типа на колекцията, който искаме от JSON Array отговор.
Например, можем да генерираме резултата като масив:
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
Или като списък :
String jsonCarArray = "[{ \"color\" : \"Black\", \"type\" : \"BMW\" }, { \"color\" : \"Red\", \"type\" : \"FIAT\" }]"; ObjectMapper objectMapper = new ObjectMapper(); List listCar = objectMapper.readValue(jsonCarArray, new TypeReference
(){}); // print cars
Повече информация за обработката на колекции с Jackson можете да намерите тук.
5. Заключение
Jackson е солидна и зряла JSON библиотека за сериализация / десериализация за Java. В ObjectMapper API предоставя просто начин да се направи разбор и да генерират JSON обекти отговор с много гъвкавост. Тази статия обсъжда основните характеристики, които правят библиотеката толкова популярна.
Изходният код, придружаващ статията, може да бъде намерен в GitHub.