المقدمة
الهياكل، أو الهياكل المتعددة، تُستخدم لجمع عدة قطع من المعلومات معًا في وحدة واحدة. تُستخدم هذه المجموعات من المعلومات لوصف مفاهيم على مستوى أعلى، مثل العنوان
المكون من الشارع
، المدينة
، الولاية
، والرمز البريدي
. عند قراءة هذه المعلومات من الأنظمة مثل قواعد البيانات أو واجهات برمجة التطبيقات، يمكنك استخدام علامات الهيكل للتحكم في كيفية تعيين هذه المعلومات إلى حقول الهيكل. تعتبر علامات الهيكل قطعًا صغيرة من البيانات المعرفية المرفقة بحقول هيكل توفر تعليمات للكود الآخر في Go الذي يعمل مع الهيكل.
كيف تبدو علامة الهيكل؟
علامات الهيكل في Go هي تعليقات تظهر بعد النوع في تعريف هيكل Go. تتألف كل علامة من سلاسل قصيرة مرتبطة بقيمة مقابلها.
A struct tag looks like this, with the tag offset with backtick `
characters:
يمكن للكود الآخر في Go بعد ذلك فحص هذه الهياكل واستخراج القيم المعينة للمفاتيح المحددة التي يطلبها. لا تؤثر علامات الهيكل على عمل الكود الخاص بك دون كود إضافي يفحصها.
جرّب هذا المثال لرؤية كيف تبدو علامات الهيكل، وأنه من دون كود من حزمة أخرى، فلن تكون لها أي تأثير.
سينتج ذلك:
OutputHi! My name is Sammy
يعرّف هذا المثال نوعًا بالاسم User
مع حقل Name
. تم إعطاء الحقل Name
علامة هيكلية بقيمة example:"name"
. سنشير إلى هذه العلامة الهيكلية المحددة في المحادثة بـ “علامة الهيكل المثالية” لأنها تستخدم الكلمة “example” كمفتاح لها. تحمل علامة الهيكل example
القيمة "name"
لحقل Name
. على نوع User
، نعرّف أيضًا طريقة String()
المطلوبة من واجهة fmt.Stringer
. سيتم استدعاء هذه الطريقة تلقائيًا عند تمرير النوع إلى fmt.Println
وتمنحنا فرصة لإنتاج نسخة مهيأة بشكل جيد من الهيكل الخاص بنا.
في جسم الدالة main
، ننشئ مثالًا جديدًا من نوعنا User
ونمرره إلى fmt.Println
. على الرغم من وجود علامة هيكلية في الهيكل، نرى أنه ليس لها تأثير على عمل هذا الكود Go. سيتصرف بالضبط بنفس الطريقة إذا كانت العلامة الهيكلية غير موجودة.
لاستخدام علامات الهيكل لتحقيق شيء ما، يجب كتابة كود Go آخر لفحص الهياكل في وقت التشغيل. تحتوي حزمة المكتبة القياسية على حزم تستخدم علامات الهيكل كجزء من عملها. الأكثر شيوعًا من هذه هي حزمة encoding/json
.
ترميز JSON
الترميز النصي لكائنات JavaScript (JSON) هو تنسيق نصي لترميز مجموعات من البيانات المنظمة تحت مفاتيح سلسلة مختلفة. يُستخدم عادة لتبادل البيانات بين برامج مختلفة نظرًا لأن التنسيق بسيط بما يكفي حتى توجد مكتبات لفك تشفيره في العديد من اللغات المختلفة. يأتي ما يلي كمثال على JSON:
{
"language": "Go",
"mascot": "Gopher"
}
يحتوي هذا الكائن JSON على مفتاحين، language
و mascot
. بعد هذه المفاتيح تتبع القيم المرتبطة. هنا، يحمل مفتاح language
قيمة Go
ويتم تعيين mascot
قيمة Gopher
.
يستخدم مُشفر JSON في مكتبة القياسية علامات البنية كتوضيحات تُشير إلى المُشفر كيف تود تسمية حقولك في الإخراج JSON. يمكن العثور على هذه الآليات لترميز وفك تشفير JSON في encoding/json
package.
جرب هذا المثال لرؤية كيفية ترميز 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
إلى string
وقمنا بتمرير السلسلة الناتجة إلى 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
إلى معلومات إضافية لتمكينها من التعرف على أن بعض الحقول يجب أن تعتبر غير معينة عندما تفترض هذه القيمة الصفرية. يمكنك إضافة ,omitempty
إلى الجزء القيمي لأي علامة هيكل json
لتخبر محول JSON بكتم إخراج هذا الحقل عندما تكون قيمة الحقل مساوية للقيمة الصفرية. يُظهر المثال التالي كيفية تصحيح الأمثلة السابقة لعدم إخراج الحقول الفارغة بعد الآن:
سيُظهر هذا المثال:
Output{
"name": "Sammy the Shark",
"password": "fisharegreat",
"createdAt": "2019-09-23T18:21:53.863846-04:00"
}
لقد قمنا بتعديل الأمثلة السابقة بحيث يحتوي الحقل PreferredFish
الآن على علامة هيكل json:"preferredFish,omitempty"
. تسبب وجود التعديل ,omitempty
في تخطي المحول JSON لهذا الحقل، نظرًا لأننا قررنا تركه غير معين. كانت قيمته 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