הקדמה
מבנים, או structs, משמשים לאיסוף מספר רב של פרטים ביחד ביחידה אחת. אוספים אלו של מידע משמשים לתיאור מושגים ברמה גבוהה יותר, כמו כתובת
המורכבת מ־רחוב
, עיר
, מדינה
, ו־מיקוד
. כאשר אתה קורא את המידע הזה ממערכות כמו מסדי נתונים או APIs, אתה יכול להשתמש ב־תגי structs כדי לשלוט באיזו דרך המידע הזה מוקצה לשדות של struct. תגי structs הם חתיכות קטנות של מטא־נתונים מצורפות לשדות של struct שמספקות הוראות לקוד Go אחר שעובד עם ה־struct.
איך נראה תג Struct?
תגי struct של Go הם הערות שמופיעות אחרי הסוג בהצהרת struct של Go. כל תג מורכב ממחרוזות קצרות המשויכות לערך תואם מסוים.
A struct tag looks like this, with the tag offset with backtick `
characters:
קוד Go אחר מסוגל לבדוק את ה־structs האלו ולחלץ את הערכים שהוקצו למפתחות מסוימים שהוא מבקש. תגי structs אין להם השפעה על פעולת הקוד שלך ללא קוד נוסף שבודק אותם.
נסה את הדוגמה הזו כדי לראות איך תגי struct נראים, ושללא קוד מחבילה אחרת, הם לא יהיו לו כל השפעה.
זה יפלט:
OutputHi! My name is Sammy
דוגמה זו מגדירה סוג User
עם שדה של Name
. לשדה Name
ניתנה תגית struct של example:"name"
. אנו נפנה לתג המסוים הזה בשיח כ "תג struct דוגמה" מכיוון שהוא משתמש במילה "דוגמה" כמפתח שלו. לתג ה- example
יש את הערך "name"
עבור השדה Name
. בסוג User
, אנו גם מגדירים את השיטה String()
הנדרשת על ידי ממשק ה- fmt.Stringer
. זה ייקרא אוטומטית כאשר אנו מעבירים את הסוג ל- fmt.Println
ונותן לנו הזדמנות לייצר גרסה מעוצבת של ה- struct שלנו.
בתוך גוף הפונקציה main
, אנו יוצרים מופע חדש של הסוג שלנו User
ומעבירים אותו ל- fmt.Println
. גם אם הסטרקט כילה תג struct, אנו רואים שהוא אין לו שום השפעה על פעולת קוד Go זו. הוא יתנהג בדיוק באותו אופן אם התג struct לא היה נוכח.
כדי להשתמש בתגי struct כדי להשיג משהו, יש לכתוב קוד Go אחר המבדוק את struct בזמן ריצה. בספריית התקנים יש חבילות שמשתמשות בתגי struct כחלק מפעולתן. הפופולרית ביותר מביניהן היא חבילת encoding/json
.
קידוד JSON
מסמך העברת מידע (JSON) הוא פורמט טקסטואלי לקידוד אוספים של נתונים שמאורגנים במפתחות מחרוזת שונים. בדרך כלל משמש לתקשורת של נתונים בין תוכניות שונות מאחר והפורמט הוא פשוט מספיק כך שספריות קיימות לקידוד שלו בשפות תכנות שונות. הנה דוגמה ל-JSON:
{
"language": "Go",
"mascot": "Gopher"
}
אובייקט ה-JSON הזה מכיל שני מפתחות, language
ו-mascot
. לאחר מכן מופיעים הערכים המשויכים. כאן המפתח language
מקבל ערך של Go
והמפתח mascot
מקבל את הערך Gopher
.
מקודד ה-JSON בספריית הסטנדרט משתמש בתגיות מבנה כהערות שמציינות למקודד כיצד ברצונך שיהיה שמך לשדות בפלט ה-JSON. המנגנון הזה של הקידוד והפיענוח של JSON ניתן למציאה בחבילת encoding/json
של.
נסה דוגמה זו כדי לראות כיצד נעשה קידוד של JSON בלי תגיות מבנה:
הפלט יראה כך:
Output{
"Name": "Sammy the Shark",
"Password": "fisharegreat",
"CreatedAt": "2019-09-23T15:50:01.203059-04:00"
}
הגדרנו מבנה המתאר משתמש עם שדות כוללים את שמו, הסיסמה שלו, והזמן שבו נוצר המשתמש. בתוך פונקציית main
, אנו יוצרים מופע של משתמש זה על ידי ספק ערכים עבור כל השדות חוץ מ־PreferredFish
(סמי אוהב את כל הדגים). לאחר מכן, אנו מעבירים את מופע ה־User
לפונקציית json.MarshalIndent
. זה משמש כדי שנוכל לראות את הפלט של JSON בצורה יותר נוחה מבלי להשתמש בכלי עיצוב חיצוני. קריאה זו יכולה להיות מוחלפת ב־json.Marshal(u)
כדי להדפיס JSON בלי שום רווחים נוספים. שני הארגומנטים הנוספים ל־json.MarshalIndent
שולטים על תחילת הפלט (שלא ציינו כאן באמצעות מחרוזת ריקה) והתווים לשימוש בכניסה, שכאן הם שני תווים רווח. כל שגיאות שיוצרות מ־json.MarshalIndent
מוזנות ליומן והתוכנית נסגרת באמצעות os.Exit(1)
. לבסוף, אנו ממירים את ה־[]byte
שחזר מ־json.MarshalIndent
למחרוזת ומעבירים את המחרוזת התוצאה ל־fmt.Println
להדפסה בטרמינל.
שדות המבנה מופיעים בדיוק כמו שקרויים. זה אינו הסגנון התקני של JSON שאתה עשוי לצפות בו, אך, שימוש בכתיבת גומרים לשמות השדות. כפי שתראה בעת הפעלת הדוגמה הזו, זה לא יעבוד מכיוון ששמות השדות הרצויים נקלעים לחוקי Go אודות שמות שדות מיוצאים.
זה יציג את הפלט הבא:
Output{}
בגרסה זו, שינינו את שמות השדות לקייס קמל. כעת Name
הוא name
, Password
הוא password
, ולבסוף CreatedAt
הוא createdAt
. בתוך גוף ה־main
, שינינו את היצירה של המבנה שלנו כך שישמשו את השמות החדשים הללו. לאחר מכן, אנו מעבירים את המבנה לפונקציית json.MarshalIndent
כפי שעשינו בעבר. הפלט, הפעם, הוא אובייקט JSON ריק, {}
.
הקיום המדויק של קייס קמל דורש שהתו הראשון יהיה באות קטנה. בעוד ש־JSON לא משנה איך אתה מכנה את השדות שלך, Go כן, מכיוון שזה מציין את הנראות של השדה מחוץ לחבילה. מאחר שחבילת encoding/json
היא חבילה נפרדת מהחבילה הראשית main
שאנו משתמשים בה, עלינו לעשות את התו הראשון באות ריקעת כדי להפוך אותו לגלוי עבור encoding/json
. נראה שאנו במבוי סתום. אנו זקוקים לאמץ להעביר למקודד JSON איזה שם אנו רוצים שהשדה יקרא.
שימוש בתגיות מבנה כדי לשלוט בקידוד
אתה יכול לשנות את הדוגמה הקודמת כך שיהיו לשדות שהוצאו מעוצבים עם שמות שדות בצורה נכונה עם תוויות מבנה על כל שדה עם תווית מבנה. התווית מבנה שencoding/json
מזהה כוללת מפתח של json
וערך ששולט בפלט. על ידי שימת גרסת הקמל של שמות השדות כערך למפתח json
, המקודד ישתמש בשם זה במקום. הדוגמה הזו מתקן את שני הניסיונות הקודמים:
זה יוציא:
Output{
"name": "Sammy the Shark",
"password": "fisharegreat",
"preferredFish": null,
"createdAt": "2019-09-23T18:16:17.57739-04:00"
}
שינינו את שדות המבנה כך שיהיו גלויים לחבילות אחרות על ידי כתיבת האותיות הראשונות של השמות שלהם. אך הפעם הוספנו תגי מבנה בצורת json:"name"
, כאשר "name"
היה השם שרצינו שjson.MarshalIndent
ישתמש בו בעת הדפסת המבנה שלנו כ-JSON.
עכשיו הצלחנו לעצב את JSON שלנו כראוי. שים לב, אך, שהשדות לערכים מסוימים הודפסו גם אם לא הגדרנו את הערכים הללו. מקודד ה-JSON יכול להסיר גם שדות אלו, אם תרצה.
הסרת שדות JSON ריקים
נפוץ להסיר את השדות שאינם מוגדרים ב־JSON. מאחר וכל סוג בִּשְׂפַת Go מכיל "ערך ריק" – ערך ברירת מחדל שהוא מוגדר אליו, החבילה encoding/json
דורשת מידע נוסף כדי להבדיל בין ערך ריק לבין שדה שאינו מוגדר. בתוך הערך של התגית ה־json
, ניתן להוסיף לשם הרצוי של השדה שלך את הסיומת ,omitempty
כדי להוריד את הפלט של השדה כאשר הערך שלו הוא הערך הריק. הדוגמה הבאה מתקנת את הדוגמאות הקודמות כך שהן כבר לא מפליטות שדות ריקים:
דוגמה זו תפליט:
Output{
"name": "Sammy the Shark",
"password": "fisharegreat",
"createdAt": "2019-09-23T18:21:53.863846-04:00"
}
עשינו שינוי בדוגמאות הקודמות כך שלשדה של PreferredFish
יש כעת את התגית הבאה: json:"preferredFish,omitempty"
. קיום ההוספה ,omitempty
גורם ל־JSON encoder לדלג על השדה הזה, מאחר שהחלטנו להשאיר אותו לא מוגדר. בדוגמאות הקודמות שלנו, הערך של השדה הזה היה null
.
הפלט נראה הרבה יותר טוב, אך אנו עדיין מדפיסים את הסיסמה של המשתמש. החבילה encoding/json
מספקת דרך נוספת להתעלם משדות פרטיים לחלוטין.
התעלמות משדות פרטיים
כמה תחומים חייבים להיות מיוצאים ממבני נתונים כדי שחבילות אחרות תוכל להתממשק כראוי עם הסוג. אף על פי שטבעם של השדות הללו עשוי להיות רגיש, כך שבמקרים אלה, נרצה שמקודד ה־JSON יתעלם לחלוטין מהשדה — גם כאשר הוא מוגדר. זה נעשה באמצעות הערך המיוחד -
כארגומנט ערך לתג מבנה json:
.
דוגמה זו מתקנת את הבעיה של חשיפת הסיסמה של המשתמש.
כאשר אתה מפעיל דוגמה זו, תראה את פלט זה:
Output{
"name": "Sammy the Shark",
"createdAt": "2019-09-23T16:08:21.124481-04:00"
}
הדבר היחיד ששינינו בדוגמה זו מהדוגמאות הקודמות הוא ששדה הסיסמה משתמש כעת בערך מיוחד "-"
עבור תג המבנה שלו json:
. בפלט מדוגמה זו אינו נמצא עוד שדה password
.
תכונות אלו של חבילת encoding/json
— ,omitempty
, "-"
, ו־אפשרויות אחרות — אינן סטנדרטיות. מה שחבילה מחליטה לעשות עם ערכים של תג מבנה תלוי ביישום שלה. מאחר וחבילת encoding/json
היא חלק מספריית הסטנדרטית, חבילות אחרות גם הן מיישמות את התכונות הללו באותה הדרך כדי שיהיה מוגדר כמקובל. עם זאת, חשוב לקרוא את התיעוד של כל חבילה של צד שלישי שמשתמשת בתגי מבנה כדי ללמוד מה נתמך ומה לא.
מסקנה
תגי מבנה מציעים אמצעי חזק להעשרת פונקציונליות של קוד העובד עם המבנים שלך. חבילות רבות של הספרייה הסטנדרטית וגורמים צד שלישי מציעות דרכים להתאים אישית את פעולתן באמצעות שימוש בתגי מבנה. השימוש היעיל בהם בקוד שלך מספק הן את התנהגות ההתאמה האישית הזו וגם מתעד בצורה תמציתית איך שדות אלו נעשים בשימוש למפתחים עתידיים.
Source:
https://www.digitalocean.com/community/tutorials/how-to-use-struct-tags-in-go