در Protobuf نسخه ۳ تمامی فیلدها اختیاری هستند، اما این اختیاری بودن همه جا به این معنی نیست که مقدار اون فیلد nil ست میشه. تو این مطلب میخوایم حالتی رو بررسی کنیم که قصد ما ارسال مقدار nil برای یک فیلده و راه حلهایی که برای این مسئله وجود داره رو ببینیم. برای مثال این پیام ساده رو در نظر بگیرید:
message Measurement {
string id = 1;
string name = 2;
int32 value = 3;
}
زمان ایجاد و مقداردهی یک نمونه از این پیام میتونیم یک یا چند فیلد اون رو مقداردهی نکنیم:
m := &pb.Measurement {
name: "heat-sensor-1"
}
مسئله مقدار صفر در golang
حالتی رو تصور کنید که سرویس A پیام بالا رو به سرویس B مخابره میکنه. مقدار value برای نمونه ارسال شده چند خواهد بود؟ از اونجایی که متغیرهای مقداردهی نشده در گولنگ به صورت مقدار صفر در نظر گرفته میشن مقدار value صفر خواهد بود. یعنی پیامی که سرویس B دریافت میکنه به صورت زیره:
*pb.Measurement {
id: "",
name: "heat-sensor-1",
value: 0,
}
اما اگر value برای سرویس A واقعا نامعلوم باشه و ما بخوایم به سرویس B این رو بگیم که value نا معلومه چه راههایی داریم؟
بستهبندی (wrapping) فیلد
تو این روش فیلد value رو با کمک یک message دیگر بستهبندی میکنیم:
با تعریف یک message جدید
با تعریف یک message جدید برای فیلد value:
message Heat {
int32 v = 1;
}
message Measurement {
string id = 1;
string name = 2;
Heat value = 3;
}
این امکان به وجود میاد که بتونیم برای value مقدار nil ارسال کنیم:
m := &pb.Measurement {
,name: "heat-sensor-1"
,value: nil
}
و پیامی که سرویس B دریافت میکنه:
*pb.Measurement {
id: "",
name: "heat-sensor-1",
value: nil,
}
با استفاده از wrappers.proto
به جای تعریف message جدید میتونیم از messageهای از پیش تعریف شده گوگل استفاده کنیم:
import "google/protobuf/wrappers.proto";
message Measurement {
string id = 1;
string name = 2;
google.protobuf.Int32Value value = 3;
}
در موارد خاص از Oneof هم میتوان برای این هدف استفاده کرد که از حوصله این مطلب خارجه.
استفاده از کلیدواژه optional در Protobuf
بعد از بحثهایی نسبتا مفصل، تیم پروتوباف راضی به اضافه کردن کلیدواژه optional شد. این ویژگی به صورت آزمایشی از نسخه ۳.۱۲ پروتوباف در دسترسه. برای استفاده از این ویژگی کافیه قبل از فیلد مورد نظرم optional رو اضافه کنیم:
message Measurement {
string id = 1;
string name = 2;
optional int32 value = 3;
}
برای کامپایل نیازه که فلگ experimental_allow_proto3_optional ست بشه:
$ protoc test.proto --go_out=plugins=grpc:./ --experimental_allow_proto3_optional
در این حالت در فایل go جنریت شده value از نوع *int خواهد بود (به جای int). حالا به راحتی میتونیم مقدار value رو nil ارسال کنیم:
m := &pb.Measurement {
,name: "heat-sensor-1"
,value: nil
}
در این روش نیازی نداریم که از یک نوع message دیگر که عملا کاربردی جز بستهبندی داده برای ما ندارد استفاده کنیم.
رفرنسها:
آخرین مطالب