Jackson Librarysi JSON yoki XML filelardan ma'lumotlarni o'qish va filelarga JSON yoki XML formatda yozish uchun ishlatiladigan Library yani kutubxonalaridan biri.
It has built in Object Mapper class which parses json files and deserializes it to custom java objects. It helps in generating json from java objects.
ObjectMapper classi objectlarni json filelarga parse qilish va ushbu ma'lumotlarni java objectlari uchun deserialize qilish uchun ishlatiladi. Ushbu class Java Objectlaridan Json yaratish uchun yordam beradi.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.3</version>
</dependency>The simple readValue API of the ObjectMapper is a good entry point. We can use it to parse or deserialize JSON content into a Java object.
Also, on the writing side, we can use the writeValue API to serialize any Java object as JSON output.
Biz objectMapper-ni readValue method orqali JSON Java object aylantirish uchun boshqacha qilib aytadigan bo'lsak Deserialize qilish uchun ishlatamiz.
writeValue methoddini esa biz Java Objectni JSON aylantirish ya'ni Serialize qilishimiz uchun ishlatamiz.
public class Car {
private String color;
private String type;
// standard getters setters
}ObjectMapper objectMapper = new ObjectMapper();
Car car = new Car("yellow","renault");
objectMapper.writeValue(new File("target/car.json"),car);{"color":"yellow","type":"renault"}The methods writeValueAsString and writeValueAsBytes of ObjectMapper class generate a JSON from a Java object and return the generated JSON as a string or as a byte array:
ObjectMapper-ni classning writeValueAsString va writeValueAsBytes methodlari Java Objectidan JSON hosil qiladi va json ni string yoki byte array sifatida qaytaradi.
String carAsString=objectMapper.writeValueAsString(car);String json="{ \"color\" : \"Black\", \"type\" : \"BMW\" }";
Car car = objectMapper.readValue(json,Car.class); Car car = objectMapper.readValue(new File("src/test/resources/json_car.json"),Car.class);Alternatively, a JSON can be parsed into a JsonNode object and used to retrieve data from a specific node:
String json="{ \"color\" : \"Black\", \"type\" : \"FIAT\" }";
JsonNode jsonNode = objectMapper.readTree(json);
String color = jsonNode.get("color").asText();
// Output: color -> BlackWe can parse a JSON in the form of an array into a Java object list using a TypeReference:
String jsonCarArray="[{ \"color\" : \"Black\", \"type\" : \"BMW\" }, { \"color\" : \"Red\", \"type\" : \"FIAT\" }]";
List<Car> listCar=objectMapper.readValue(jsonCarArray,new TypeReference<List<Car>>(){});Similarly, we can parse a JSON into a Java Map:
String json="{ \"color\" : \"Black\", \"type\" : \"BMW\" }";
Map<String, Object> map=objectMapper.readValue(json,new TypeReference<Map<String, Object>>(){});One of the greatest strengths of the Jackson library is the highly customizable serialization and deserialization process.
Jackson Librarysining eng kuchli xususiyatlaridan biri Serialize va Deserialize qilish jarayoni customize qilishimiz mumkin.
While converting JSON objects to Java classes, in case the JSON string has some new fields, the default process will result in an exception. Through the configure method, we can extend the default process to ignore the new fields:
Agar biz JSON objectlarni Java objectlarga Deserialize qilyotgan paytimizda, agar JSON da yangi fieldlar bo'lsa exception tashlaydi. UnrecognizedPropertyException
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);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<Car> {
public CustomCarSerializer() {
this(null);
}
public CustomCarSerializer(Class<Car> t) {
super(t);
}
@Override
public void serialize(
Car car, JsonGenerator jsonGenerator, SerializerProvider serializer) {
jsonGenerator.writeStartObject();
jsonGenerator.writeStringField("car_brand", car.getType());
jsonGenerator.writeEndObject();
}
}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);var carJson = {"car_brand":"renault"}Custom Deserializer
public class CustomCarDeserializer extends StdDeserializer<Car> {
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;
}
}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);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.
public class Request {
private String carName;
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
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"}Another small but useful feature available through the DeserializationFeature class is the ability to generate the type of collection we want from a JSON Array response.
For example, we can generate the result as an 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);or List
String jsonCarArray =
"[{ \"color\" : \"Black\", \"type\" : \"BMW\" }, { \"color\" : \"Red\", \"type\" : \"FIAT\" }]";
ObjectMapper objectMapper = new ObjectMapper();
List<Car> listCar = objectMapper.readValue(jsonCarArray, new TypeReference<List<Car>>(){});Jackson allow us to control this behavior at either the class level:
@JsonInclude(Include.NON_NULL)
public class MyDto { ... }Or with more granularity at the field level:
public class MyDto {
@JsonInclude(Include.NON_NULL)
private String stringValue;
private int intValue;
// standard getters and setters
}Jackson also allows us to configure this behavior globally on the ObjectMapper:
mapper.setSerializationInclusion(Include.NON_NULL);Now any null field in any class serialized through this mapper is going to be ignored
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(Include.NON_NULL);
MyDto dtoObject = new MyDto();
String dtoAsString = mapper.writeValueAsString(dtoObject);
assertThat(dtoAsString, containsString("intValue"));
assertThat(dtoAsString, containsString("booleanValue"));
assertThat(dtoAsString, not(containsString("stringValue")));Ignoring null fields is such a common Jackson configuration because it's often the case that we need to have better control over the JSON output.
public class MyDto {
private String stringValue;
public MyDto() {
super();
}
public String getStringValue() {
return stringValue;
}
public void setStringValue(String stringValue) {
this.stringValue = stringValue;
}
}Serializing it will result in the following JSON
{"stringValue":"some value"}To customize that output so that, instead of stringValue we get – for example – strVal, we need to simply annotate the getter
@JsonProperty("strVal")
public String getStringValue() {
return stringValue;
}Now, on serialization, we will get the desired output
{"strVal":"some value"}ObjectMapper mapper = new ObjectMapper();
MyDtoFieldNameChanged dtoObject = new MyDtoFieldNameChanged();
dtoObject.setStringValue("a");
String dtoAsString = mapper.writeValueAsString(dtoObject);
assertThat(dtoAsString, not(containsString("stringValue")));
assertThat(dtoAsString, containsString("strVal"));The simplest way to make sure a field is both serializable and deserializable is to make it public.
public class MyDtoAccessLevel {
private String stringValue;
int intValue;
protected float floatValue;
public boolean booleanValue;
// NO setters or getters
}Out of the four fields of the class, just the public booleanValue will be serialized to JSON by default
ObjectMapper mapper = new ObjectMapper();
MyDtoAccessLevel dtoObject = new MyDtoAccessLevel();
String dtoAsString = mapper.writeValueAsString(dtoObject);
assertThat(dtoAsString, not(containsString("stringValue")));
assertThat(dtoAsString, not(containsString("intValue")));
assertThat(dtoAsString, not(containsString("floatValue")));
assertThat(dtoAsString, containsString("booleanValue"));Now, another simple way to make a field – especially a non-public field – serializable, is to add a getter for it
public class MyDtoWithGetter {
private String stringValue;
private int intValue;
public String getStringValue() {
return stringValue;
}
}We now expect the stringValue field to be serializable, while the other private field not to be, as it has no getter
@Test
public void givenDifferentAccessLevels_whenGetterAdded_thenSerializable()
throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
MyDtoGetter dtoObject = new MyDtoGetter();
String dtoAsString = mapper.writeValueAsString(dtoObject);
assertThat(dtoAsString, containsString("stringValue"));
assertThat(dtoAsString, not(containsString("intValue")));
}Unintuitively, the getter also makes the private field deserializable as well – because once it has a getter, the field is considered a property.
We saw how the getter made the private field both serializable and deserializable. On the other hand, a setter will only mark the non-public field as deserializable
public class MyDtoWithSetter {
private int intValue;
public void setIntValue(int intValue) {
this.intValue = intValue;
}
public int accessIntValue() {
return intValue;
}
}As you can see, the private intValue field only has a setter this time. We do have a way to access the value, but that's not a standard getter.
The unmarshalling process for intValue should work correctly
@Test
public void givenDifferentAccessLevels_whenSetterAdded_thenDeserializable()
throws JsonProcessingException, JsonMappingException, IOException {
String jsonAsString = "{\"intValue\":1}";
ObjectMapper mapper = new ObjectMapper();
MyDtoSetter dtoObject = mapper.readValue(jsonAsString, MyDtoSetter.class);
assertThat(dtoObject.anotherGetIntValue(), equalTo(1));
}And as we mentioned, the setter should only make the field deserializable, but not serializable:
@Test
public void givenDifferentAccessLevels_whenSetterAdded_thenStillNotSerializable()
throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
MyDtoSetter dtoObject = new MyDtoSetter();
String dtoAsString = mapper.writeValueAsString(dtoObject);
assertThat(dtoAsString, not(containsString("intValue")));
}In some cases where, for example, you might not actually be able to modify the source code directly – we need to configure the way Jackson deals with non-public fields from the outside.
That kind of global configuration can be done at the ObjectMapper level, by turning on the AutoDetect function to use either public fields or getter/setter methods for serialization, or maybe turn on serialization for all fields
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, Visibility.NONE);
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);The following test case verifies all member fields (including non-public) of MyDtoAccessLevel are serializable
@Test
public void givenDifferentAccessLevels_whenSetVisibility_thenSerializable()
throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
MyDtoAccessLevel dtoObject = new MyDtoAccessLevel();
String dtoAsString = mapper.writeValueAsString(dtoObject);
assertThat(dtoAsString, containsString("stringValue"));
assertThat(dtoAsString, containsString("intValue"));
assertThat(dtoAsString, containsString("booleanValue"));
}The following example shows a User object which contains sensitive password information which shouldn't be serialized to JSON.
To get there, we simply add the @JsonIgnore annotation on the getter of the password, and enable deserialization for the field by applying the @JsonProperty annotation on the setter
@JsonIgnore
public String getPassword() {
return password;
}
@JsonProperty
public void setPassword(String password) {
this.password = password;
}Now the password information won’t be serialized to JSON
@Test
public void givenFieldTypeIsIgnoredOnlyAtSerialization_whenUserIsSerialized_thenIgnored()
throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
User userObject = new User();
userObject.setPassword("thePassword");
String userAsString = mapper.writeValueAsString(userObject);
assertThat(userAsString, not(containsString("password")));
assertThat(userAsString, not(containsString("thePassword")));
}the JSON containing the password will be successfully deserialized to the User object
@Test
public void givenFieldTypeIsIgnoredOnlyAtSerialization_whenUserIsDeserialized_thenCorrect()
throws JsonParseException, JsonMappingException, IOException {
String jsonAsString = "{\"password\":\"thePassword\"}";
ObjectMapper mapper = new ObjectMapper();
User userObject = mapper.readValue(jsonAsString, User.class);
assertThat(userObject.getPassword(), equalTo("thePassword"));
}