1. ภาพรวม
บทช่วยสอนนี้มุ่งเน้นไปที่การทำความเข้าใจคลาสJackson ObjectMapperและวิธีการทำให้อนุกรมออบเจ็กต์ Java เป็น JSON และแยกสตริง JSON ลงในออบเจ็กต์ Java
เพื่อทำความเข้าใจเพิ่มเติมเกี่ยวกับห้องสมุด Jackson โดยทั่วไป Jackson Tutorial เป็นจุดเริ่มต้นที่ดี
2. การพึ่งพา
ก่อนอื่นเรามาเพิ่มการอ้างอิงต่อไปนี้ในpom.xml :
com.fasterxml.jackson.core jackson-databind 2.11.1
การพึ่งพานี้จะเพิ่มไลบรารีต่อไปนี้ลงใน classpath:
- แจ็คสันคำอธิบายประกอบ
- แจ็คสันคอร์
ใช้เวอร์ชันล่าสุดจาก Maven central repository สำหรับjackson-databindเสมอ
3. การอ่านและเขียนโดยใช้ObjectMapper
เริ่มต้นด้วยการอ่านและเขียนขั้นพื้นฐาน
readValue API อย่างง่ายของObjectMapperเป็นจุดเริ่มต้นที่ดี เราสามารถใช้เพื่อแยกวิเคราะห์หรือแยกเนื้อหา JSON ลงในออบเจ็กต์ Java
นอกจากนี้ในด้านการเขียนเราสามารถใช้writeValue API เพื่อทำให้ออบเจ็กต์ Java เป็นอนุกรมเป็นเอาต์พุต JSON
เราจะใช้คลาสCarต่อไปนี้กับสองฟิลด์เป็นอ็อบเจกต์ในการทำให้เป็นอนุกรมหรือ deserialize ตลอดบทความนี้:
public class Car { private String color; private String type; // standard getters setters }
3.1. Java Object เป็น 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 Object
ด้านล่างนี้เป็นตัวอย่างง่ายๆของการแปลง 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 เป็น Jackson 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
เราสามารถแยกวิเคราะห์ JSON ในรูปแบบของอาร์เรย์ลงในรายการวัตถุ Java โดยใช้TypeReference :
String jsonCarArray = "[{ \"color\" : \"Black\", \"type\" : \"BMW\" }, { \"color\" : \"Red\", \"type\" : \"FIAT\" }]"; List listCar = objectMapper.readValue(jsonCarArray, new TypeReference
(){});
3.5. การสร้างแผนที่ Java จากสตริง JSON
ในทำนองเดียวกันเราสามารถแยกวิเคราะห์ JSON ลงใน Java Map :
String json = "{ \"color\" : \"Black\", \"type\" : \"BMW\" }"; Map map = objectMapper.readValue(json, new TypeReference
4. คุณสมบัติขั้นสูง
จุดแข็งที่ยิ่งใหญ่ที่สุดอย่างหนึ่งของไลบรารีแจ็คสันคือกระบวนการทำให้เป็นอนุกรมและดีซีเรียลไลเซชันที่ปรับแต่งได้สูง
ในส่วนนี้เราจะพูดถึงคุณลักษณะขั้นสูงบางอย่างที่อินพุตหรือการตอบสนอง JSON ของเอาต์พุตอาจแตกต่างจากออบเจ็กต์ที่สร้างหรือใช้การตอบสนอง
4.1. การกำหนดค่าคุณสมบัติ Serialization หรือ Deserialization
ในขณะที่แปลงออบเจ็กต์ 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 เป็นไลบรารีอนุกรม / deserialization JSON ที่มั่นคงและสมบูรณ์สำหรับ Java ObjectMapper API ให้เป็นวิธีที่ตรงไปตรงมาแยกและสร้างวัตถุตอบสนอง JSON ที่มีความยืดหยุ่นมาก บทความนี้กล่าวถึงคุณสมบัติหลักที่ทำให้ห้องสมุดได้รับความนิยม
ซอร์สโค้ดที่มาพร้อมกับบทความสามารถพบได้ใน GitHub