ارسال JSON در Jax-Rs جاوا با استفاده از Jackson
از سری مقالات آموزش Java Enterprise, مبحث Jackson در جاوا را بر میگزینیم.
در مقاله قبل نتیجه را در قالب XML ارسال کردیم. اما استفاده از قالب JSON در سمت Client متداول تر است. به این ترتیب نیاز است تا پاسخ ها در قالب JSON را به RESTful API اضافه کنیم. برای تبدیل نتایج به قالب JSON از Jackson استفاده میکنیم. با استفاده از Jackson میتوان عمل mapping میان object ها و JSON را به آسانی صورت داد.
در این مقاله قصد داریم تا پردازش JSON توسط Jackson را بررسی کنیم و همچنین از annotation های موجود در Jackson استفاده کنیم. این annotation ها در mapping به ما کمک میکنند. در ابتدای مقاله باید مفهوم Plain Old Java Object و یا به اختصار POJO را بررسی کنیم. POJO یک کلاس از جاوا است که در آن, دسترسی به متغیر ها از طریق متد های getter و setter امکان پذیر است. تفاوت این نوع کلاس با Java Beans در این است که Java Beans باید یک Constructor بدون متغیر داشته باشد و همچنین حتما باید Serializable باشد. اما در POJO این قوانین الزامی نیست. به بیان ساده تر هر Java Beans یک POJO است.
برای شروع, نیاز است تا maven Dependency برای Jackson را به Jax-Rs اضافه کنیم:
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.media/jersey-media-json-jackson --> <dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-json-jackson</artifactId> <version>2.25.1</version> </dependency>
اکنون برای بررسی خروجی, متد getProfiles را به این صورت تغییر میدهیم:
@Path("profiles") public class UserResource { @GET @Produces(MediaType.APPLICATION_JSON) public List<UserModel> getProfiles() { return UserService.getModels(); } }
با اجرای کد فوق, خروجی در قالب JSON نمایش داده میشود. در اینجا Jackson تبدیل کلاس ما به JSON را انجام داده است. اگر Maven Dependency مربوط به آن را به پروژه اضافه نکنیم, با خطا زیر مواجه میشویم:
MessageBodyWriter not found for media type=application/json,
این خطا نشان میدهد که Jax-Rs به تنهایی قادر به Mapping تمام کلاس به JSON نیست.
اکنون از Jackson Annotation استفاده میکنیم تا بتوانیم بهتر عمل Mapping را انجام دهیم. Maven Dependency برای Annotation ها را اضافه میکنیم:
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.8.8</version> </dependency>
Json Any Getter Annotation این امکان را فراهم میسازد تا از تمام مقادیر کلاس Map به صورت استاندارد استفاده کنیم. فرض کنید چنین متدی را به کلاس UserModel اضافه کنیم:
public HashMap<String, String> getFileds() { fileds = new HashMap<>(); fileds.put("filed 1", "value 1"); fileds.put("field 2", "value 2"); fileds.put("filed 3", "value 3"); return fileds; }
و خروجی به این صورت خواهد بود:
username: "Vhdrjb", location: "Iran", code: 99, name: "Vahid Rajabi", fileds: { field 1: "Value 1", field 2: "value 2", field 3: "Value 3" }
اما اگر Json Any Getter Annotation را به پروژه اضافه کنیم:
@JsonAnyGetter public HashMap<String, String> getFileds() { return fileds; }
خروجی به این صورت خواهد بود:
username: "Vhdrjb", location: "Iran", code: 99, name: "Vahid Rajabi", field 1: "Value 1", field 2: "value 2", field 3: "Value 3"
همانطور که مشاهده میکنید, مقادیر درون Map را به صورت تک متغیر در نظر گرفته است.
با استفاده از Json Getter Annotation میتوانیم مشخص کنیم که برای یک متغیر خاص, کدام متد به عنوان Getter آن متغیر باشد:
@JsonGetter("name") public String getName() { return "My Name ".concat(name); }
و مقدار خروجی به این صورت خواهد بود:
name: "My Name Vahid Rajabi",
Json Property Order Annotation, ترتیب متغیر ها در خروجی را مشخص میکند:
@JsonPropertyOrder({"name", "username","code","location","fields"})
برای استفاده از JSON در Client ها, باید برای هر Object یک اسم تعریف شود. برای مثال وقتی از Gson برای پردازش Json استفاده میکنید, نیاز است تا با توجه به نام object های json, عمل mapping را انجام دهیم. تعریف نام برای object از طریق json root name annotation انجام میشود:
@JsonRootName("User") public class UserModel { ... }
برای اینکه root name در خروجی نمایش داده شود, باید Serialization feature آن را به متد resource اضافه کنیم:
@JacksonFeatures(serializationEnable = SerializationFeature.WRAP_ROOT_VALUE) public List<UserModel> getProfiles() {
در بعضی موارد نیاز است تا برای یک نوع داده ای, Serialization خاصی را اعمال کنیم. برای مثال در یک کلاس model میخواهیم نمایش تاریخ به صورت ساعت و دقیقه باشد. میتوان این خروجی را با استفاده از متد ها پیاده سازی کرد. همچنین با استفاده از کلاس Json Serializer میتوانیم نتیجه json را در قالب خاصی مشخص کنیم. یک کلاس DateSerializer ایجاد میکنیم:
public class DateSerializer extends JsonSerializer<Date> { @Override public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException { SimpleDateFormat dateFormat = new SimpleDateFormat("hh:mm"); jsonGenerator.writeString(dateFormat.format(date)); } }
در کد بالا, با استفاده از کلاس Simple Date Format یک ساختار برای Date مشخص کردهایم. سپس مشخص میکنیم که json generator چنین ساختاری را در خروجی چاپ کند. برای اعمال ساختار این کلاس, از json serializer annotation استفاده میکنیم:
@JsonSerialize(using = DateSerializer.class) private Date date;
برای استفاده از متغیر date, دیگر نیاز نیست تا مقادیر خاصی را به آن ارجاع دهیم. برای دریافت زمان کنونی, یک instance از Date ایجاد میکنیم و به صورت خودکار, این تبدیل انجام میشود.
در بعضی موارد, نیاز است تا یکسری از متغیر ها, در خروجی چاپ نشوند. برای مثال کاربر دریافت کننده json نیازی به id کاربر ندارد. با استفاده از json ignore میتوانیم چنین متغیر هایی را مشخص کنیم:
@JsonIgnore private int code;
این annotation در سطح متغیر عمل میکند. اما اگر بخواهیم چندین متغیر را ignore کنیم, نوشتن چندین annotation ممکن است آزار دهنده باشد. برای رفع این چالش, از json ignore properties استفاده میکنیم:
@JsonIgnoreProperties({"date","location"}) public class UserModel { ... }
در این مقاله سعی بر این شد تا با استفاده از Jackson در Jax-RS, خروجی را در قالب json برگردانیم.
سری مقالات آموزش Java Beans ادامه دارد.
با ما همراه باشید.
دیدگاهتان را بنویسید
برای نوشتن دیدگاه باید وارد بشوید.