ถ้านึกจะเขียนแอปฯที่เอาไว้เรียกข้อมูลจาก Web Service ก็คงไม่พ้น Retrofit ยอดนิยมที่คอมโบคู่กับ Gson เพื่อแปลงข้อมูลจาก JSON ให้กลายเป็น Object (Model Class) และในบทความนี้ก็จะมาพูดถึง @SerializedName
ของ Gson เมื่อต้องใช้ ProGuard กันครับ
เจ้าของบล็อกก็เป็นคนหนึ่งที่ประทับใจใน @SerializedName
มากๆ เพราะว่ามันแก้ปัญหาเรื่อง Key ใน JSON ไม่ตรงกับชื่อของตัวแปรใน Object เพราะว่าวิธีการตั้งชื่อของทั้งสองอย่างนั้นไม่เหมือนกันหรืออยากจะเปลี่ยนเป็นชื่ออื่นเลยก็ทำได้เช่นกัน
public class AwesomeProfile implements Parcelable {
@SerializedName("html_url")
private String githubUrl;
/* ... */
}
และเจ้าของบล็อกก็เจอบ่อยที่นักพัฒนาใช้ @SerializedName
บ้างหรือไม่ใช้บ้างแบบนี้
public class AwesomeProfile implements Parcelable {
private String login;
private String id;
@SerializedName("avatar_url")
private String avatarUrl;
@SerializedName("html_url")
private String githubUrl;
private String bio;
private String email;
private String blog;
private String company;
private String name;
@SerializedName("public_repos")
private int publicRepos;
@SerializedName("public_gists")
private int publicGists;
private int followers;
private int following;
/* ... */
}
ถ้าดูเผินๆก็คงไม่คิดอะไรมาก เพราะว่าจะใช้ @SerializedName
ไปทำไม ในเมื่อชื่อตัวแปรมันก็ตรงกับ Key ใน JSON อยู่แล้ว และมันก็สามารถทำงานได้ปกติสุข
จนกระทั่งใส่ ProGuard ตอน Release ขึ้น Production…
ปัญหาจะผุดขึ้นมาทันทีเมื่อต้องใส่ ProGuard ตอนจะเอาขึ้น Production เพราะว่าจะมีขั้นตอน Obfuscate ที่แปลงชื่อตัวแปรและชื่อคลาสให้อ่านยากขึ้น ดังนั้นเมื่อเอาไฟล์ Release APK มา Decompile ดู ก็จะเห็นโค้ดที่เปลี่ยนไปดังนี้
public class a implements Parcelable {
private String a;
private String b;
@c(a = "avatar_url")
private String c;
@c(a = "html_url")
private String d;
private String e;
private String f;
private String g;
private String h;
private String i;
@c(a = "public_repos")
private int j;
@c(a = "public_gists")
private int k;
private int l;
private int m;
/* ... */
}
จะเห็นว่าชื่อต่างๆถูกแปลงให้เป็นตัวอักษรสั้นๆทั้งหมดเลย ขนาด @SerializedName ยังถูกแปลงเป็น @c
เลย
และนั่นหมายความว่า Gson จะแปลงข้อมูลผิดทันที เพราะว่าชื่อตัวแปรไม่ตรงกับ Key ใน JSON มีแค่อันที่ใส่ @SerializedName
เท่านั้นที่ยังถูกต้อง เพราะชื่อ Key ที่กำหนดไว้ในนั้นเป็น String ธรรมดาๆ
เมื่อแปลงข้อมูลไม่ได้ ค่าที่ไม่ได้กำหนด @SerializedName
ก็จะมีค่าเป็น Null ไปโดยปริยาย และทำให้แอปฯเกิด NullPointerException ได้ทันที
ใส่ @Keep เพื่อป้องกัน Obfuscate จาก ProGuard
โดยปกติแล้ว Model หรือ Data Class มักจะเป็นคลาสที่เก็บข้อมูลเพียงอย่างเดียว ไม่ได้มี Logic อะไรที่สำคัญอยู่ข้างใน ดังนั้นโดยปกติแล้วจึงไม่จำเป็นต้อง Obfuscate เลยซักนิดเดียว
ดังนั้นผู้ที่หลงเข้ามาอ่านจึงสามารถใช้ @Keep
เพื่อเลี่ยงการ Obfuscate ได้เลย
@Keep
public class AwesomeProfile implements Parcelable {
private String login;
private String id;
@SerializedName("avatar_url")
private String avatarUrl;
@SerializedName("html_url")
/* ... */
}
หรือจะใส่ @SerializedName
ให้ครบทุกตัวก็ได้ (แต่จะเหนื่อยกว่า)