FPC מגיע עם תמיכה מאוד חזקה בJSON. המימוש הוא מימוש טבעי ואינו תלוי בספרייה כלשהי. החלטתי להציג כיצד אפשר לעבוד איתו על קצה המזלג. לקח לי כרבע שעה מהרגע שהחלטתי שאני רוצה ללמוד לעשות את זה, עד שקיבלתי את התוצר שאתם רואים. כמובן שלפני כן, לא היה לי ניסיון בכלל לעבוד עם הספרייה המגיעה עם FPC. ואם זה לא מספיק, השתמשתי ב kate המגיע עם KDE ולא ב Lazarus לפיתוח – מה שלקח ממני קצת יותר זמן.
הספרייה עצמה מגיעה עם מספר יחידות. היחידה הבסיסית והנדרשת כמעט תמיד היא fpJSON, אשר מספקת לנו אובייקטים המתאימים ל json עצמו. יש את jsonscanner אשר נבנה בשביל לפרש קבצי json. זהו מפרש לקסאלי. יש את jsonparser אשר נועד לפרש את המידע מ jsonscanner ולהעביר אותו לאובייקטים המוגדרים ב fpJSON.
בנוסף, הספרייה מגיעה עם fpJSONRTTI אשר ממיר אובייקטים באמצעות RTTI ל JSON וממיר חזרה מ JSON לאובייקטים. בנוסף מגיעה בספרייה גם יחידה בשם jsonconf בשביל לעבוד עם קבצי הגדרות מבוססי json.
אז כיצד אני יוצר מחרוזת JSON מאובייקטי פסקל אתם בטח שואלים. הנה קוד פשוט:
program create_json;
{$mode objfpc}
uses fpjson, SysUtils;
var
json : TJSONObject;
begin
json := TJsonObject.Create;
try
json.add('Hello', 'World');
json.add('TheAnswerForWorldUniverseAndEverything', 42);
json.add('Array', TJSONArray.Create(['a', 1, 3.14]));
writeln('JSON: ', json.AsJSON);
except
on e:EJSON do
writeln(stderr, 'Unable to create json: ', e.message);
end;
FreeAndNil(json);
end.
יצרנו מחלקה מסוג TJSONObject אשר בעצם מייצגת "אובייקט" של JSON בתקן עצמו. משם הוספנו איבר מסוג מחרוזת, איבר מסוג מספר שלם ומאיבר מסוג מערך. המערך מכיל בתוכו גם מחרוזת, גם מספר שלם וגם מספר עשרוני.
בשביל להיות בטוחים שהעבודה נעשתה כמו שצריך, אנחנו תופסים אותה לחריגות כלשהם, ובמידה והתיה בעיה, אז אנחנו נציג עליה מידע.
לפרש קובץ json זה עוד יותר פשוט, אבל יש משהו קטן שצריך לשים לב אליו:
program parse_json;
{$mode objfpc}
uses fpjson, jsonparser, SysUtils, classes;
var
parser : TJSONParser;
json_file : TFileStream;
json : TJSONData;
begin
json_file := TFileStream.Create('/tmp/test.json', fmOpenRead);
json_file.position := 0;
parser := TJSONParser.create(json_file);
try
json := parser.Parse;
writeln('JSON: ', json.AsJSON);
except
on e:EJSONParser do
writeln(stderr, 'Unable to parse JSON: ', e.message);
end;
FreeAndNil(json);
FreeAndNil(parser);
FreeAndNil(json_file);
end.
הסיבה שאנחנו שמים את המידע שמחוזר אלינו כאובייקטי JSON ומשחררים לבד, הוא בגלל שParse היא פונקציה אשר יוצרת עבורינו במיוחד אובייקט מוכן, אבל היא לא מנהלת אותו, אלא מעבירה אלינו את הניהול, מה שאומר שאנחנו אחראים לשחרר את הזיכרון כאשר אנחנו מפסיקים להשתמש בו. חשוב להבין כי זה אומר שאנחנו יכולים לשחרר את המפרש מוקדם יותר מאשר שחרור האובייקט שקיבלנו.
עוד דבר חשוב, הוא לדעת לתפוס שגיאות במידה ויש בזמן ניסיון קריאת הקובץ והניסיון לפרש אותו. במידה ולא נעשה זאת, התכנית שלנו תמות לפני זמנה.
FreeAndNil?
לא באמת קשור לנושא הרשומה, אבל מעניין אותי מה תחשוב על הפוסט הזה:
http://www.nickhodges.com/post/Using-FreeAndNil.aspx
(אני עצמי השתמשתי עד היום ב-FreeAndNil "ליתר ביטחון", וכנראה שאמשיך לעשות זאת כי אני לא מתכנת עד כדי כך מנוסה, אבל יש היגיון מסוים בטיעון שלו)
אני לא מסכים איתו. nil הוא לא תוספת אשר לא צריך אותה. זה לא מדבר על שחרור, זה מדבר על בדיקה. נגיד ואעשה if assigned… אז אם האובייקט עבר free אבל הוא לא nil, אז לרוב assgined יכנס, אבל אין לו מה להריץ, ואז במקרה הטוב אקבל AccessViolation ובמקרה הרע, יהיה לי code execution שאני לא אהיה מודע אליו. nil בא ואומר כי לא רק ששחררתי את הזכרון, אלא אני לא מצביע לשום דבר. ואם אני לא מצביע לשום דבר ולא שחררתי זיכרון, אז יש לי דליפת זיכרון, ואם שחררתי אבל לא עבדתי עם nil, ויש מצב שאבדוק אם האובייקט מאותחל, אז אני צפוי במקרה הטוב לחריגה שתעצור את הריצה, ובמקרה הרע אני מחביא לעצמי באג כלשהו.
זו לפחות דעתי בניסיון שיש לי. אה, ובלזרוס הוסיפו ל lclproc את FreeThenNil, שהוא לדעתי הגיוני יותר לשימוש 🙂
FreeThenNil – רעיון נחמד! 🙂
אני יודע שהשמת nil מבטיחה ש-Assigned ייתן תשובה מועילה… אבל ניק טוען בפוסט שלו שזה בדיוק העניין: שבתכנון נכון לא מגיעים בכלל למצב שצריך לשאול אם אובייקט הוא Assigned (הפונקציה עצמה עדיין שימושית, רק לא למטרה הספציפית הזאת).
ארתיום – זו פעם ראשונה שאני שומע על מתקפה שמבוססת על פוינטרים תועים. זה בהחלט מעניין ותודה על הקישור, אם כי אני עדיין לא בטוח שזה סותר את הטיעון שבפוסט – כי "בתכנון הנכון" אתה הרי לא תיגש לעולם לאובייקט ששחררת (בניגוד לפונקציות וירטואליות שמוזכרות בוויקיפדיה ושם באמת יש סיכון).
אני אולי זקן מידי, אבל אני למדתי לא לסמוך על עצמי לא לעשות טעויות ולא לעשות באגים. וזה עוד לפני לדעת מי אולי ישתמש בקוד שלי. אני לא חושב שאני יכול "לתכנן נכון" או בעיקר לסמוך על זה. עדיף לעשות עוד צעד ולשים כ nil, מאשר להתעורר יום אחד ולגלות באג שקיים מהיום הראשון שהסתרת אותו. אבל זה רק אני כמובן 🙂
לא רק אתה, אני בטוח.
הסיבה היחידה שאני מתייחס בכובד ראש לדברים היא שמדובר באדם שיודע דבר או שניים על דלפי ופסקל.
הבעיה היא שהוא חושב בגישה אקדמית מידי. תניח שתמיד המצב יהיה אופטימלי, ואני מעולם לא עבדתי במצב אופטימלי עד היום. אצלי תמיד מישהו היה לחוץ על העבודה, תמיד קורים מצבים שאני נוגע בקוד שלא ראיתי שנתיים וצריך פתאום לתחזק אותו, ואין הרבה זמן …
הגישה שלו לא מתאימה למציאות, ואין לה קשר לפסקל או דלפי, אלא לחשיבה שלדעתי דומה מידי לאקדמיה
השמה של Nil או NULL למצביע נועדה למנוע מתקפה שבה אם פונים למבציע הלא מאופס, טכנית אפשר לגרום להרצת קוד זדוני. במקרה של גישה למצביע מאופס, התוכנה פשוט תעוף אבל לא תיווצר סיכוי להרצת קוד.
ראה: http://en.wikipedia.org/wiki/Dangling_pointer