توجه: این سایت منسوخ شده است. این سایت پس از 31 ژانویه 2023 غیرفعال می شود و ترافیک به سایت جدید در https://protobuf.dev هدایت می شود. در ضمن، به روز رسانی ها فقط برای protobuf.dev انجام می شود.

راهنمای زبان (proto3)

با مجموعه‌ها، منظم بمانید ذخیره و دسته‌بندی محتوا براساس اولویت‌های شما.

این راهنما نحوه استفاده از زبان بافر پروتکل برای ساختار داده‌های بافر پروتکل، از جمله نحو فایل .proto و نحوه تولید کلاس‌های دسترسی به داده از فایل‌های .proto را شرح می‌دهد. این نسخه پروتکل پروتکل بافرهای زبان را پوشش می‌دهد: برای اطلاعات در مورد دستور proto2 ، به راهنمای زبان Proto2 مراجعه کنید.

این یک راهنمای مرجع است - برای مثالی گام به گام که از بسیاری از ویژگی های شرح داده شده در این سند استفاده می کند، به آموزش زبان انتخابی خود مراجعه کنید (در حال حاضر فقط proto2؛ مستندات پروتو3 بیشتری به زودی ارائه می شود).

تعریف یک نوع پیام

ابتدا به یک مثال بسیار ساده نگاه می کنیم. فرض کنید می خواهید فرمت پیام درخواست جستجو را تعریف کنید، که در آن هر درخواست جستجو دارای یک رشته پرس و جو، صفحه خاصی از نتایج مورد نظر شما و تعدادی نتیجه در هر صفحه است. در اینجا فایل .proto است که برای تعریف نوع پیام استفاده می کنید.

syntax = "proto3";

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}
  • خط اول فایل مشخص می کند که شما از دستور proto3 استفاده می کنید: اگر این کار را انجام ندهید، کامپایلر بافر پروتکل فرض می کند که از proto2 استفاده می کنید. این باید اولین خط غیر خالی و بدون نظر فایل باشد.
  • تعریف پیام SearchRequest سه فیلد (جفت نام/مقدار) را مشخص می کند، یکی برای هر قطعه داده ای که می خواهید در این نوع پیام قرار دهید. هر فیلد یک نام و یک نوع دارد.

تعیین انواع فیلدها

در مثال بالا، همه فیلدها از نوع اسکالر هستند : دو عدد صحیح ( page_number و result_per_page ) و یک رشته ( query ). با این حال، شما همچنین می توانید انواع ترکیبی را برای فیلدهای خود، از جمله شمارش و سایر انواع پیام، مشخص کنید.

تخصیص شماره فیلدها

همانطور که می بینید، هر قسمت در تعریف پیام دارای یک شماره منحصر به فرد است. این شماره‌های فیلد برای شناسایی فیلدهای شما در قالب باینری پیام استفاده می‌شوند و پس از استفاده از نوع پیام شما نباید تغییر داده شوند. توجه داشته باشید که اعداد فیلد در محدوده 1 تا 15 یک بایت برای رمزگذاری نیاز دارند، از جمله شماره فیلد و نوع فیلد (شما می توانید در مورد این موضوع در Protocol Buffer Encoding اطلاعات بیشتری کسب کنید). اعداد فیلد در محدوده 16 تا 2047 دو بایت می گیرند. بنابراین باید اعداد 1 تا 15 را برای عناصر پیامی که اغلب اتفاق می‌افتند رزرو کنید. به یاد داشته باشید که برای عناصر متداول که ممکن است در آینده اضافه شوند، کمی فضا بگذارید.

کوچکترین عدد فیلدی که می توانید تعیین کنید 1 است و بزرگترین آن 2 29 - 1 یا 536,870,911 است. همچنین نمی توانید از اعداد 19000 تا 19999 استفاده کنید ( FieldDescriptor::kFirstReservedNumber تا FieldDescriptor::kLastReservedNumber )، زیرا آنها برای اجرای Protocol Buffers رزرو شده اند—اگر از یکی از این اعداد رزرو شده در .proto استفاده کنید، کامپایلر بافر پروتکل شکایت خواهد کرد. به همین ترتیب، نمی توانید از شماره های فیلد رزرو شده قبلی استفاده کنید.

تعیین قوانین فیلد

فیلدهای پیام می تواند یکی از موارد زیر باشد:

  • singular : یک پیام خوب می تواند صفر یا یک این فیلد (اما نه بیشتر از یک) داشته باشد. هنگام استفاده از دستور proto3، این قانون فیلد پیش‌فرض زمانی است که هیچ قانون فیلدی دیگری برای یک فیلد مشخص نشده است. شما نمی توانید تعیین کنید که آیا از سیم تجزیه شده است یا خیر. به سیم سریالی می شود مگر اینکه مقدار پیش فرض باشد. برای اطلاعات بیشتر در مورد این موضوع، به حضور در میدان مراجعه کنید.
  • optional : همان singular ، با این تفاوت که می توانید بررسی کنید که آیا مقدار به صراحت تنظیم شده است یا خیر. یک فیلد optional در یکی از دو حالت ممکن است:
    • فیلد تنظیم شده است و حاوی مقداری است که به صراحت از سیم تنظیم یا تجزیه شده است. به سیم سریال خواهد شد.
    • فیلد تنظیم نشده است و مقدار پیش فرض را برمی گرداند. به سیم سریالی نمی شود.
  • repeated شده: این نوع فیلد را می توان صفر یا چند بار در یک پیام خوب تکرار کرد. ترتیب مقادیر تکرار شده حفظ خواهد شد.
  • map : این یک نوع فیلد کلید/مقدار جفت شده است. برای اطلاعات بیشتر در مورد این نوع فیلد به Maps مراجعه کنید.

در proto3، فیلدهای repeated انواع عددی اسکالر به طور پیش فرض از رمزگذاری packed استفاده می کنند. می توانید در مورد رمزگذاری packed در Protocol Buffer Encoding اطلاعات بیشتری کسب کنید.

افزودن انواع پیام بیشتر

انواع پیام های متعدد را می توان در یک فایل .proto تعریف کرد. اگر چندین پیام مرتبط را تعریف می‌کنید، مفید است - بنابراین، برای مثال، اگر می‌خواهید قالب پیام پاسخ را که با نوع پیام SearchResponse شما مطابقت دارد تعریف کنید، می‌توانید آن را به همان .proto اضافه کنید:

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}

message SearchResponse {
 ...
}

افزودن نظرات

برای افزودن نظرات به فایل‌های .proto خود، از سینتکس C/C++-style // و /* ... */ استفاده کنید.

/* SearchRequest represents a search query, with pagination options to
 * indicate which results to include in the response. */

message SearchRequest {
  string query = 1;
  int32 page_number = 2;  // Which page number do we want?
  int32 result_per_page = 3;  // Number of results to return per page.
}

فیلدهای رزرو شده

اگر یک نوع پیام را با حذف کامل یک فیلد یا اظهار نظر در آن به‌روزرسانی کنید، کاربران آینده می‌توانند هنگام ایجاد به‌روزرسانی‌های خود، از شماره فیلد دوباره استفاده کنند. اگر بعداً نسخه‌های قدیمی همان .proto را بارگیری کنند، می‌تواند مشکلات جدی ایجاد کند، از جمله خرابی داده‌ها، اشکالات حریم خصوصی، و غیره. یکی از راه‌های اطمینان از اینکه این اتفاق نمی‌افتد این است که مشخص کنید شماره فیلدها (و/یا نام‌ها، که می‌تواند باعث ایجاد مشکل در سریال‌سازی JSON نیز شود) فیلدهای حذف شده شما reserved شده باشد. کامپایلر بافر پروتکل اگر هر یک از کاربران آینده سعی کنند از این شناسه‌های فیلد استفاده کنند، شکایت خواهد کرد.

message Foo {
  reserved 2, 15, 9 to 11;
  reserved "foo", "bar";
}

توجه داشته باشید که نمی توانید نام فیلدها و شماره فیلدها را در همان عبارت reserved شده ترکیب کنید.

چه چیزی از .proto شما تولید می شود؟

هنگامی که کامپایلر بافر پروتکل را روی .proto ، کامپایلر کدی را به زبان انتخابی شما تولید می‌کند که برای کار با انواع پیام‌هایی که در فایل توضیح داده‌اید، از جمله دریافت و تنظیم مقادیر فیلد، سریال‌سازی پیام‌های شما به آن نیاز دارید. یک جریان خروجی، و تجزیه پیام های شما از یک جریان ورودی.

  • برای C++ ، کامپایلر یک فایل .h و .cc از هر .proto ، با یک کلاس برای هر نوع پیام توضیح داده شده در فایل شما تولید می کند.
  • برای جاوا ، کامپایلر یک .java . با یک کلاس برای هر نوع پیام، و همچنین یک کلاس Builder ویژه برای ایجاد نمونه های کلاس پیام تولید می کند.
  • برای Kotlin ، علاوه بر کد تولید شده جاوا، کامپایلر برای هر نوع پیام یک فایل .kt . حاوی یک DSL تولید می کند که می تواند برای ساده سازی ایجاد نمونه های پیام استفاده شود.
  • پایتون کمی متفاوت است - کامپایلر پایتون یک ماژول با یک توصیفگر ایستا از هر نوع پیام در .proto شما ایجاد می کند، که سپس با یک متاکلاس برای ایجاد کلاس دسترسی به داده های پایتون لازم در زمان اجرا استفاده می شود.
  • برای Go ، کامپایلر یک فایل .pb.go با یک نوع برای هر نوع پیام در فایل شما تولید می کند.
  • برای Ruby ، کامپایلر یک فایل .rb با یک ماژول روبی حاوی انواع پیام شما تولید می کند.
  • برای Objective-C ، کامپایلر یک فایل pbobjc.h و pbobjc.m از هر .proto می کند، با یک کلاس برای هر نوع پیام که در فایل شما توضیح داده شده است.
  • برای C# ، کامپایلر یک فایل .proto .cs با یک کلاس برای هر نوع پیام که در فایل شما توضیح داده شده، تولید می کند.
  • برای Dart ، کامپایلر یک فایل .pb.dart با یک کلاس برای هر نوع پیام در فایل شما تولید می کند.

می توانید با دنبال کردن آموزش زبان انتخابی خود (نسخه های proto3 به زودی) درباره استفاده از API برای هر زبان اطلاعات بیشتری کسب کنید. برای جزئیات بیشتر API، به مرجع API مربوطه مراجعه کنید (نسخه های proto3 نیز به زودی ارائه می شوند).

انواع ارزش اسکالر

یک فیلد پیام اسکالر می تواند یکی از انواع زیر را داشته باشد - جدول نوع مشخص شده در فایل .proto و نوع مربوطه را در کلاس تولید شده به طور خودکار نشان می دهد:

نوع پروتو یادداشت نوع C++ جاوا/کوتلین نوع [1] نوع پایتون [3] برو تایپ کن نوع یاقوت نوع سی شارپ نوع PHP نوع دارت
دو برابر دو برابر دو برابر شناور float64 شناور دو برابر شناور دو برابر
شناور شناور شناور شناور float32 شناور شناور شناور دو برابر
int32 از رمزگذاری با طول متغیر استفاده می کند. برای رمزگذاری اعداد منفی ناکارآمد - اگر فیلد شما احتمالاً مقادیر منفی دارد، به جای آن از sint32 استفاده کنید. int32 بین المللی بین المللی int32 Fixnum یا Bignum (در صورت نیاز) بین المللی عدد صحیح بین المللی
int64 از رمزگذاری با طول متغیر استفاده می کند. برای رمزگذاری اعداد منفی ناکارآمد - اگر فیلد شما احتمالاً مقادیر منفی دارد، به جای آن از sint64 استفاده کنید. int64 طولانی int/long [4] int64 بیگنوم طولانی عدد صحیح/رشته [6] Int64
uint32 از رمزگذاری با طول متغیر استفاده می کند. uint32 int [2] int/long [4] uint32 Fixnum یا Bignum (در صورت نیاز) unint عدد صحیح بین المللی
uint64 از رمزگذاری با طول متغیر استفاده می کند. uint64 طولانی [2] int/long [4] uint64 بیگنوم طولانی عدد صحیح/رشته [6] Int64
sint32 از رمزگذاری با طول متغیر استفاده می کند. ارزش int امضا شده این اعداد منفی نسبت به int32 های معمولی کارآمدتر هستند. int32 بین المللی بین المللی int32 Fixnum یا Bignum (در صورت نیاز) بین المللی عدد صحیح بین المللی
sint64 از رمزگذاری با طول متغیر استفاده می کند. ارزش int امضا شده اینها نسبت به int64 های معمولی اعداد منفی را با کارایی بیشتری رمزگذاری می کنند. int64 طولانی int/long [4] int64 بیگنوم طولانی عدد صحیح/رشته [6] Int64
ثابت 32 همیشه چهار بایت. کارآمدتر از uint32 اگر مقادیر اغلب بیشتر از 2 28 باشد. uint32 int [2] int/long [4] uint32 Fixnum یا Bignum (در صورت نیاز) unint عدد صحیح بین المللی
ثابت 64 همیشه هشت بایت. کارآمدتر از uint64 اگر مقادیر اغلب بیشتر از 2 56 باشد. uint64 طولانی [2] int/long [4] uint64 بیگنوم طولانی عدد صحیح/رشته [6] Int64
sfixed32 همیشه چهار بایت. int32 بین المللی بین المللی int32 Fixnum یا Bignum (در صورت نیاز) بین المللی عدد صحیح بین المللی
sfixed64 همیشه هشت بایت. int64 طولانی int/long [4] int64 بیگنوم طولانی عدد صحیح/رشته [6] Int64
بوول بوول بولی بوول بوول کلاس TrueClass/FalseClass بوول بولی بوول
رشته یک رشته باید همیشه حاوی متن کدگذاری شده UTF-8 یا 7 بیتی ASCII باشد و نمی تواند بیشتر از 2 32 باشد. رشته رشته خیابان/یونیکد [5] رشته رشته (UTF-8) رشته رشته رشته
بایت ها ممکن است حاوی هر دنباله دلخواه بایت باشد که بیش از 2 32 نباشد. رشته ByteString خیابان (Python 2)
بایت (Python 3)
[]بایت رشته (ASCII-8BIT) ByteString رشته فهرست کنید

هنگامی که پیام خود را در Protocol Buffer Encoding سریالی می کنید، می توانید درباره نحوه کدگذاری این انواع اطلاعات بیشتری کسب کنید.

[1] کاتلین از انواع مربوطه از جاوا، حتی برای انواع بدون علامت، برای اطمینان از سازگاری در پایگاه‌های کد مختلط جاوا/کاتلین استفاده می‌کند.

[2] در جاوا، اعداد صحیح 32 بیتی و 64 بیتی بدون علامت با استفاده از همتایان امضا شده خود نشان داده می شوند و بیت بالایی به سادگی در بیت علامت ذخیره می شود.

[3] در همه موارد، تنظیم مقادیر برای یک فیلد، بررسی نوع را انجام می دهد تا از معتبر بودن آن اطمینان حاصل شود.

[4] اعداد صحیح 32 بیتی 64 بیتی یا بدون علامت همیشه هنگام رمزگشایی به مدت طولانی نشان داده می شوند، اما اگر در هنگام تنظیم فیلد یک int داده شود، می توانند int باشند. در همه موارد، مقدار باید با نوع نمایش داده شده در هنگام تنظیم مطابقت داشته باشد. [2] را ببینید.

[5] رشته‌های پایتون در رمزگشایی به‌عنوان یونیکد نشان داده می‌شوند، اما اگر یک رشته ASCII داده شود، می‌توانند str باشند (این ممکن است تغییر کند).

[6] عدد صحیح در ماشین های 64 بیتی و رشته در ماشین های 32 بیتی استفاده می شود.

مقادیر پیش فرض

هنگامی که یک پیام تجزیه می شود، اگر پیام رمزگذاری شده حاوی عنصر منحصر به فرد خاصی نباشد، فیلد مربوطه در شی تجزیه شده روی مقدار پیش فرض آن فیلد تنظیم می شود. این پیش‌فرض‌ها مختص نوع هستند:

  • برای رشته ها، مقدار پیش فرض رشته خالی است.
  • برای بایت ها، مقدار پیش فرض بایت خالی است.
  • برای bools، مقدار پیش فرض false است.
  • برای انواع عددی، مقدار پیش فرض صفر است.
  • برای enums ، مقدار پیش فرض اولین مقدار enum تعریف شده است که باید 0 باشد.
  • برای فیلدهای پیام، فیلد تنظیم نشده است. مقدار دقیق آن وابسته به زبان است. برای جزئیات به راهنمای کد تولید شده مراجعه کنید.

مقدار پیش‌فرض برای فیلدهای تکراری خالی است (معمولاً یک لیست خالی در زبان مناسب).

توجه داشته باشید که برای فیلدهای پیام اسکالر، هنگامی که یک پیام تجزیه می شود، هیچ راهی برای تشخیص اینکه آیا یک فیلد به صراحت روی مقدار پیش فرض تنظیم شده است (مثلاً آیا یک بولی روی false تنظیم شده است) یا اصلاً تنظیم نشده است، وجود ندارد. به هنگام تعریف انواع پیام خود توجه کنید. به عنوان مثال، اگر نمی‌خواهید آن رفتار به‌طور پیش‌فرض نیز اتفاق بیفتد، یک Boolean نداشته باشید که وقتی روی false تنظیم می‌شود، برخی از رفتارها را روشن می‌کند. همچنین توجه داشته باشید که اگر یک فیلد پیام اسکالر روی پیش‌فرض تنظیم شود، مقدار روی سیم سریالی نمی‌شود.

راهنمای کد تولید شده برای زبان انتخابی خود را برای جزئیات بیشتر در مورد نحوه عملکرد پیش فرض ها در کد تولید شده مشاهده کنید.

شمارش ها

هنگامی که یک نوع پیام را تعریف می کنید، ممکن است بخواهید یکی از فیلدهای آن فقط دارای یکی از لیست های از پیش تعریف شده مقادیر باشد. برای مثال، فرض کنید می‌خواهید برای هر SearchRequest یک فیلد corpus اضافه کنید، جایی که مجموعه می‌تواند UNIVERSAL ، WEB ، IMAGES ، LOCAL ، NEWS ، PRODUCTS یا VIDEO باشد. شما می توانید این کار را به سادگی با اضافه کردن یک enum به تعریف پیام خود با یک ثابت برای هر مقدار ممکن انجام دهید.

در مثال زیر یک enum به نام Corpus با تمام مقادیر ممکن و یک فیلد از نوع Corpus اضافه کرده‌ایم:

enum Corpus {
  CORPUS_UNSPECIFIED = 0;
  CORPUS_UNIVERSAL = 1;
  CORPUS_WEB = 2;
  CORPUS_IMAGES = 3;
  CORPUS_LOCAL = 4;
  CORPUS_NEWS = 5;
  CORPUS_PRODUCTS = 6;
  CORPUS_VIDEO = 7;
}
message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
  Corpus corpus = 4;
}

همانطور که می بینید، اولین نگاشت ثابت Corpus enum به صفر می رسد: هر تعریف enum باید دارای یک ثابت باشد که به عنوان اولین عنصر آن به صفر نگاشت می شود. این بخاطر این است که:

  • باید مقدار صفر وجود داشته باشد تا بتوانیم از 0 به عنوان مقدار پیش فرض عددی استفاده کنیم.
  • مقدار صفر باید اولین عنصر باشد، برای سازگاری با معناشناسی proto2 که در آن اولین مقدار enum همیشه پیش فرض است.

شما می توانید با تخصیص مقدار یکسان به ثابت های enum مختلف، نام مستعار را تعریف کنید. برای انجام این کار باید گزینه allow_alias را روی true تنظیم کنید، در غیر این صورت کامپایلر پروتکل با یافتن نام مستعار یک پیام خطا ایجاد می کند. اگرچه همه مقادیر مستعار در طول سریال‌زدایی معتبر هستند، اولین مقدار همیشه هنگام سریال‌سازی استفاده می‌شود.

enum EnumAllowingAlias {
  option allow_alias = true;
  EAA_UNSPECIFIED = 0;
  EAA_STARTED = 1;
  EAA_RUNNING = 1;
  EAA_FINISHED = 2;
}
enum EnumNotAllowingAlias {
  ENAA_UNSPECIFIED = 0;
  ENAA_STARTED = 1;
  // ENAA_RUNNING = 1;  // Uncommenting this line will cause a compile error inside Google and a warning message outside.
  ENAA_FINISHED = 2;
}

ثابت های شمارشگر باید در محدوده یک عدد صحیح 32 بیتی باشند. از آنجایی که مقادیر enum از رمزگذاری varint روی سیم استفاده می کنند، مقادیر منفی ناکارآمد هستند و بنابراین توصیه نمی شوند. می‌توانید enum enum می‌توانند در هر تعریف پیامی در فایل .proto شما دوباره استفاده شوند. همچنین می توانید با استفاده از نحو _MessageType_._EnumType_ از نوع enum اعلام شده در یک پیام به عنوان نوع فیلد در یک پیام دیگر استفاده کنید.

هنگامی که کامپایلر بافر پروتکل را روی .proto می کنید که از یک enum استفاده می کند، کد تولید شده دارای یک enum متناظر برای جاوا، کاتلین یا C++ یا یک کلاس EnumDescriptor ویژه برای پایتون خواهد بود که برای ایجاد مجموعه ای از ثابت های نمادین با عدد صحیح استفاده می شود. مقادیر در کلاس تولید شده توسط زمان اجرا

در طول سریال‌زدایی، مقادیر enum شناسایی‌نشده در پیام حفظ می‌شوند، اگرچه نحوه نمایش آن در صورت عدم سریال‌سازی پیام به زبان بستگی دارد. در زبان هایی که از انواع enum باز با مقادیر خارج از محدوده نمادهای مشخص شده پشتیبانی می کنند، مانند C++ و Go، مقدار enum ناشناخته به سادگی به عنوان نمایش عدد صحیح زیرین آن ذخیره می شود. در زبان‌هایی با انواع enum بسته مانند جاوا، یک case در enum برای نمایش یک مقدار ناشناخته استفاده می‌شود و می‌توان به عدد صحیح زیرین با دسترسی‌های ویژه دسترسی داشت. در هر صورت، اگر پیام سریال باشد، مقدار ناشناخته همچنان با پیام سریالی می شود.

برای اطلاعات بیشتر در مورد نحوه کار با پیام enum در برنامه های خود، راهنمای کد تولید شده برای زبان انتخابی خود را ببینید.

ارزش های رزرو شده

اگر یک نوع enum را با حذف کامل یک ورودی enum یا نظر دادن آن به‌روزرسانی کنید، کاربران آینده می‌توانند هنگام به‌روزرسانی‌های خود برای نوع، از مقدار عددی دوباره استفاده کنند. اگر بعداً نسخه‌های قدیمی همان .proto را بارگیری کنند، می‌تواند مشکلات جدی ایجاد کند، از جمله خرابی داده‌ها، اشکالات حریم خصوصی، و غیره. یکی از راه‌های اطمینان از اینکه این اتفاق نمی‌افتد این است که مشخص کنید مقادیر عددی (و/یا نام‌ها، که می‌تواند باعث ایجاد مشکل در سریال‌سازی JSON نیز شود) ورودی‌های حذف‌شده شما reserved شده باشد. کامپایلر بافر پروتکل اگر هر یک از کاربران آینده سعی کنند از این شناسه ها استفاده کنند شکایت خواهد کرد. می توانید تعیین کنید که محدوده مقدار عددی رزرو شده شما با استفاده از کلمه کلیدی max به حداکثر مقدار ممکن برسد.

enum Foo {
  reserved 2, 15, 9 to 11, 40 to max;
  reserved "FOO", "BAR";
}

توجه داشته باشید که نمی توانید نام فیلدها و مقادیر عددی را در همان عبارت reserved شده ترکیب کنید.

استفاده از انواع پیام های دیگر

می توانید از انواع پیام های دیگر به عنوان انواع فیلد استفاده کنید. برای مثال، فرض کنید می‌خواهید پیام‌های Result را در هر پیام SearchResponse اضافه کنید - برای این کار، می‌توانید یک نوع پیام Result را در همان .proto تعریف کنید و سپس یک فیلد از نوع Result را در SearchResponse مشخص کنید:

message SearchResponse {
  repeated Result results = 1;
}

message Result {
  string url = 1;
  string title = 2;
  repeated string snippets = 3;
}

تعاریف وارداتی

در مثال بالا، نوع پیام Result در همان فایل SearchResponse تعریف شده است - اگر نوع پیامی که می خواهید به عنوان نوع فیلد استفاده کنید قبلاً در یک فایل .proto دیگر تعریف شده باشد، چه؟

با وارد کردن آنها می توانید از تعاریف سایر فایل های .proto استفاده کنید. برای وارد کردن تعاریف .proto دیگر، یک عبارت import را به بالای فایل خود اضافه می کنید:

import "myproject/other_protos.proto";

به‌طور پیش‌فرض، می‌توانید از تعاریف فقط از فایل‌های .proto مستقیماً وارد شده‌اند استفاده کنید. با این حال، گاهی اوقات ممکن است لازم باشد فایل .proto را به مکان جدیدی منتقل کنید. به جای انتقال مستقیم فایل .proto و به روز رسانی تمام سایت های تماس با یک تغییر، می توانید یک مکان نگهدار فایل .proto را در مکان قدیمی قرار دهید تا با استفاده از مفهوم import public ، همه واردات را به مکان جدید ارسال کنید.

توجه داشته باشید که عملکرد واردات عمومی در جاوا در دسترس نیست.

هر کدی که پروتو حاوی بیانیه import public را وارد می‌کند، می‌تواند به صورت گذرا به وابستگی‌های import public اعتماد کند. مثلا:

// new.proto
// All definitions are moved here
// old.proto
// This is the proto that all clients are importing.
import public "new.proto";
import "other.proto";
// client.proto
import "old.proto";
// You use definitions from old.proto and new.proto, but not other.proto

کامپایلر پروتکل فایل های وارد شده را در مجموعه ای از دایرکتوری های مشخص شده در خط فرمان کامپایلر پروتکل با استفاده از پرچم -I / --proto_path می کند. اگر هیچ پرچمی داده نشده باشد، در دایرکتوری که کامپایلر در آن فراخوانی شده است، مشاهده می شود. به طور کلی باید پرچم --proto_path را روی ریشه پروژه خود تنظیم کنید و از نام های کاملاً واجد شرایط برای همه واردات استفاده کنید.

استفاده از انواع پیام proto2

این امکان وجود دارد که انواع پیام های proto2 را وارد کنید و از آنها در پیام های proto3 خود استفاده کنید و بالعکس. با این حال، proto2 enums را نمی‌توان مستقیماً در دستور proto3 استفاده کرد (اگر یک پیام پروتو2 وارد شده از آنها استفاده کند، اشکالی ندارد).

انواع تو در تو

می توانید انواع پیام را در سایر انواع پیام تعریف و استفاده کنید، مانند مثال زیر - در اینجا پیام Result در داخل پیام SearchResponse تعریف شده است:

message SearchResponse {
  message Result {
    string url = 1;
    string title = 2;
    repeated string snippets = 3;
  }
  repeated Result results = 1;
}

اگر می خواهید از این نوع پیام خارج از نوع پیام والد آن استفاده مجدد کنید، آن را به عنوان _Parent_._Type_ :

message SomeOtherMessage {
  SearchResponse.Result result = 1;
}

می‌توانید پیام‌ها را تا جایی که می‌خواهید عمیق قرار دهید:

message Outer {                  // Level 0
  message MiddleAA {  // Level 1
    message Inner {   // Level 2
      int64 ival = 1;
      bool  booly = 2;
    }
  }
  message MiddleBB {  // Level 1
    message Inner {   // Level 2
      int32 ival = 1;
      bool  booly = 2;
    }
  }
}

به روز رسانی یک نوع پیام

اگر یک نوع پیام موجود دیگر تمام نیازهای شما را برآورده نمی کند - برای مثال، می خواهید قالب پیام یک قسمت اضافی داشته باشد - اما همچنان می خواهید از کد ایجاد شده با قالب قدیمی استفاده کنید، نگران نباشید! به روز رسانی انواع پیام ها بدون شکستن کدهای موجود بسیار ساده است. فقط قوانین زیر را به خاطر بسپارید:

  • شماره فیلدها را برای فیلدهای موجود تغییر ندهید.
  • اگر فیلدهای جدیدی اضافه کنید، هر پیامی که با استفاده از قالب پیام "قدیمی" شما توسط کد سریال شده است، همچنان می تواند توسط کد تولید شده جدید شما تجزیه شود. شما باید مقادیر پیش‌فرض این عناصر را در نظر داشته باشید تا کد جدید بتواند به درستی با پیام‌های تولید شده توسط کد قدیمی تعامل داشته باشد. به طور مشابه، پیام‌های ایجاد شده توسط کد جدید شما می‌توانند توسط کد قدیمی شما تجزیه شوند: باینری‌های قدیمی به سادگی فیلد جدید را هنگام تجزیه نادیده می‌گیرند. برای جزئیات بیشتر به بخش فیلدهای ناشناخته مراجعه کنید.
  • تا زمانی که شماره فیلد دوباره در نوع پیام به روز شده شما استفاده نشود، فیلدها قابل حذف هستند. ممکن است بخواهید به جای آن نام فیلد را تغییر دهید، شاید پیشوند "OBSOLETE_" را اضافه کنید، یا شماره فیلد را رزرو کنید، به طوری که کاربران بعدی .proto . شما نتوانند تصادفاً از شماره مجددا استفاده کنند.
  • int32 ، uint32 ، int64 ، uint64 ، و bool همگی سازگار هستند - این بدان معناست که شما می توانید یک فیلد را از یکی از این انواع به دیگری بدون شکستن سازگاری رو به جلو یا عقب تغییر دهید. اگر عددی از سیم تجزیه شود که در نوع مربوطه نمی گنجد، همان اثری را خواهید داشت که اگر عدد را به آن نوع در C++ فرستاده بودید (به عنوان مثال، اگر یک عدد 64 بیتی به عنوان یک عدد خوانده شود. int32، به 32 بیت کوتاه می شود).
  • sint32 و sint64 با یکدیگر سازگار هستند اما با سایر انواع عدد صحیح سازگار نیستند.
  • string و bytes تا زمانی که بایت ها UTF-8 معتبر باشند سازگار هستند.
  • پیام‌های جاسازی‌شده در صورتی با bytes سازگار هستند که بایت‌ها دارای نسخه رمزگذاری‌شده پیام باشند.
  • fixed32 با sfixed32 و fixed64 با sfixed64 است.
  • برای string ، bytes و فیلدهای پیام، فیلدهای تکی با فیلدهای repeated سازگار هستند. با توجه به داده های سریالی یک فیلد مکرر به عنوان ورودی، کلاینت هایی که انتظار دارند این فیلد تکی باشد، آخرین مقدار ورودی را اگر یک فیلد نوع ابتدایی باشد یا تمام عناصر ورودی را اگر یک فیلد نوع پیام باشد، ادغام می کنند. توجه داشته باشید که این به طور کلی برای انواع عددی، از جمله bools و enums ایمن نیست. فیلدهای تکراری از انواع عددی را می توان در قالب بسته بندی شده سریال کرد، که در صورت انتظار یک فیلد تکی به درستی تجزیه نمی شود.
  • enum از نظر فرمت سیم با int32 ، uint32 ، int64 و uint64 سازگار است (توجه داشته باشید که اگر مقادیر مناسب نباشند، کوتاه می شوند). با این حال، توجه داشته باشید که کد مشتری ممکن است با آنها رفتار متفاوتی داشته باشد، زمانی که پیام از سریال خارج می‌شود: برای مثال، انواع proto3 enum ناشناخته در پیام حفظ می‌شوند، اما نحوه نمایش آن در صورت عدم سریال‌سازی پیام به زبان بستگی دارد. فیلدهای int همیشه فقط ارزش خود را حفظ می کنند.
  • تغییر یک فیلد یا پسوند optional منفرد به عضوی از یکی از یک عضو جدید oneof سازگار است، اما برای برخی از زبان ها (به ویژه Go) API کد تولید شده به روش های ناسازگار تغییر می کند. به همین دلیل، Google چنین تغییراتی را در APIهای عمومی خود، همانطور که در AIP-180 مستند شده است، ایجاد نمی کند. با همین اخطار در مورد سازگاری با منبع، انتقال چندین فیلد به یک oneof جدید ممکن است ایمن باشد اگر مطمئن باشید که هیچ کدی بیش از یک را در یک زمان تنظیم نمی کند. انتقال فیلدها به یکی از oneof از آنها ایمن نیست. به همین ترتیب، تغییر یک فیلد oneof به یک فیلد یا پسوند optional بی خطر است.

فیلدهای ناشناخته

فیلدهای ناشناخته داده های سریالی بافر پروتکلی هستند که فیلدهایی را نشان می دهند که تجزیه کننده آنها را تشخیص نمی دهد. به عنوان مثال، وقتی یک باینری قدیمی داده های ارسال شده توسط یک باینری جدید را با فیلدهای جدید تجزیه می کند، آن فیلدهای جدید به فیلدهای ناشناخته در باینری قدیمی تبدیل می شوند.

در ابتدا، پیام‌های proto3 همیشه فیلدهای ناشناخته را در حین تجزیه حذف می‌کردند، اما در نسخه 3.5 ما حفظ فیلدهای ناشناخته را برای مطابقت با رفتار proto2 دوباره معرفی کردیم. در نسخه های 3.5 و بالاتر، فیلدهای ناشناخته در طول تجزیه حفظ می شوند و در خروجی سریال گنجانده می شوند.

هر

Any نوع پیام به شما امکان می‌دهد از پیام‌ها به‌عنوان انواع جاسازی‌شده استفاده کنید، بدون اینکه تعریف اولیه آنها را داشته باشید. Any حاوی یک پیام سریال دلخواه به صورت bytes ، همراه با یک URL است که به عنوان یک شناسه منحصربه‌فرد جهانی برای نوع پیام عمل می‌کند و به آن پاسخ می‌دهد. برای استفاده از Any نوع، باید google/protobuf/any.proto را وارد کنید.

import "google/protobuf/any.proto";

message ErrorStatus {
  string message = 1;
  repeated google.protobuf.Any details = 2;
}

URL نوع پیش‌فرض برای یک نوع پیام خاص type.googleapis.com/_packagename_._messagename_ است.

پیاده‌سازی‌های زبان مختلف از کمک‌کننده‌های کتابخانه زمان اجرا برای بسته‌بندی و باز کردن Any مقدار به شیوه‌ای امن پشتیبانی می‌کنند – برای مثال، در جاوا، Any type دارای دسترسی‌های ویژه pack() و unpack( unpack() خواهد بود، در حالی که در C++ PackFrom() و UnpackTo() دارد. UnpackTo() روش ها:

// Storing an arbitrary message type in Any.
NetworkErrorDetails details = ...;
ErrorStatus status;
status.add_details()->PackFrom(details);

// Reading an arbitrary message from Any.
ErrorStatus status = ...;
for (const google::protobuf::Any& detail : status.details()) {
  if (detail.Is<NetworkErrorDetails>()) {
    NetworkErrorDetails network_error;
    detail.UnpackTo(&network_error);
    ... processing network_error ...
  }
}

در حال حاضر کتابخانه های زمان اجرا برای کار با Any نوع در دست توسعه هستند .

اگر قبلاً با دستور proto2 آشنا هستید، Any می‌تواند پیام‌های proto3 دلخواه را نگه دارد، مشابه پیام‌های proto2 که می‌توانند پسوندها را مجاز کنند.

یکی از

اگر پیامی دارید که دارای چندین فیلد است و حداکثر یک فیلد در آن به طور همزمان تنظیم می شود، می توانید این رفتار را اعمال کنید و با استفاده از ویژگی oneof حافظه را ذخیره کنید.

یکی از فیلدها مانند فیلدهای معمولی هستند به جز همه فیلدهای یک حافظه اشتراکی، و حداکثر می توان یک فیلد را همزمان تنظیم کرد. تنظیم هر یک از اعضای oneof به طور خودکار سایر اعضا را پاک می کند. بسته به زبان انتخابی شما، می‌توانید بررسی کنید که کدام مقدار در یکی از (در صورت وجود) با استفاده از روش خاص case() یا WhichOneof() شده است.

توجه داشته باشید که اگر چندین مقدار تنظیم شده باشد، آخرین مقدار مجموعه که با ترتیب در پروتو تعیین شده است، همه مقادیر قبلی را بازنویسی می کند.

با استفاده از Oneof

برای تعریف یکی از .proto خود از کلمه کلیدی oneof و به دنبال نام oneof خود استفاده می کنید، در این مورد test_oneof :

message SampleMessage {
  oneof test_oneof {
    string name = 4;
    SubMessage sub_message = 9;
  }
}

سپس یکی از فیلدهای خود را به تعریف oneof اضافه کنید. می توانید فیلدهایی را از هر نوع اضافه کنید، به جز فیلدهای map و فیلدهای repeated .

در کد تولید شده شما، یکی از فیلدها همان دریافت کننده و تنظیم کننده فیلدهای معمولی است. شما همچنین یک روش ویژه برای بررسی اینکه کدام مقدار (در صورت وجود) در یکی از آنها تنظیم شده است، دریافت می کنید. می توانید در مورد یکی از API برای زبان انتخابی خود در مرجع API مربوطه اطلاعات بیشتری کسب کنید.

یکی از ویژگی ها

  • تنظیم یک فیلد oneof به طور خودکار همه اعضای دیگر را از oneof پاک می کند. بنابراین اگر چند فیلد را تنظیم کنید، فقط آخرین فیلدی که تنظیم کرده اید همچنان دارای مقدار خواهد بود.

    SampleMessage message;
    message.set_name("name");
    CHECK_EQ(message.name(), "");
    // Calling mutable_sub_message() will clear the name field and will set
    // sub_message to a new instance of SubMessage with none of its fields set
    message.mutable_sub_message();
    CHECK(message.name().empty());
    
  • اگر تجزیه کننده با چندین عضو از یک واحد روی سیم روبرو شود، تنها آخرین عضوی که مشاهده می شود در پیام تجزیه شده استفاده می شود.

  • یکی از آنها قابل repeated نیست.

  • API های Reflection برای یکی از فیلدها کار می کنند.

  • اگر یک فیلد oneof را روی مقدار پیش‌فرض تنظیم کنید (مانند تنظیم یک فیلد int32 oneof روی 0)، "مورد" آن فیلد oneof تنظیم می‌شود و مقدار روی سیم سریال‌سازی می‌شود.

  • اگر از C++ استفاده می کنید، مطمئن شوید که کد شما باعث خرابی حافظه نمی شود. کد نمونه زیر خراب می شود زیرا sub_message قبلاً با فراخوانی set_name() حذف شده است.

    SampleMessage message;
    SubMessage* sub_message = message.mutable_sub_message();
    message.set_name("name");      // Will delete sub_message
    sub_message->set_...            // Crashes here
    
  • مجدداً در C++، اگر دو پیام Swap() را با oneofs تعویض کنید، هر پیام به حالت oneof دیگری ختم می‌شود: در مثال زیر، msg1 یک sub_message و msg2 یک name خواهد داشت.

    SampleMessage msg1;
    msg1.set_name("name");
    SampleMessage msg2;
    msg2.mutable_sub_message();
    msg1.swap(&msg2);
    CHECK(msg1.has_sub_message());
    CHECK_EQ(msg2.name(), "");
    

مشکلات سازگاری با عقب

هنگام افزودن یا حذف یکی از فیلدها مراقب باشید. اگر بررسی مقدار یکی از None / NOT_SET را برمی گرداند، می تواند به این معنی باشد که یکی از تنظیم نشده است یا در یک فیلد در نسخه دیگری از oneof تنظیم شده است. هیچ راهی برای تشخیص تفاوت وجود ندارد، زیرا هیچ راهی برای دانستن اینکه آیا یک فیلد ناشناخته روی سیم عضو یکی از آن ها است یا خیر وجود ندارد.

مشکلات استفاده مجدد را تگ کنید

  • انتقال فیلدها به داخل یا خارج از یکی از : ممکن است برخی از اطلاعات خود را از دست بدهید (برخی فیلدها پاک خواهند شد) پس از سریال و تجزیه پیام. با این حال، می‌توانید با خیال راحت یک فیلد را به یک فیلد جدید منتقل کنید و ممکن است بتوانید چندین فیلد را جابه‌جا کنید، اگر بدانید که فقط یک فیلد تنظیم شده است. برای جزئیات بیشتر به به روز رسانی یک نوع پیام مراجعه کنید.
  • یک فیلد یکی را حذف کنید و دوباره آن را اضافه کنید : ممکن است پس از سریال‌سازی و تجزیه پیام، فیلد یکی از تنظیم‌شده فعلی شما پاک شود.
  • تقسیم یا ادغام یکی از : این کار مشکلاتی مشابه جابجایی فیلدهای معمولی دارد.

نقشه ها

اگر می خواهید یک نقشه انجمنی به عنوان بخشی از تعریف داده خود ایجاد کنید، بافرهای پروتکل یک دستور میانبر مفید را ارائه می دهند:

map<key_type, value_type> map_field = N;

... که در آن key_type می تواند هر نوع انتگرال یا رشته ای باشد (بنابراین، هر نوع اسکالر به جز انواع ممیز شناور و bytes ). توجه داشته باشید که enum یک key_type معتبر نیست. value_type می تواند هر نوع دیگری باشد به جز نقشه دیگری.

بنابراین، برای مثال، اگر می‌خواهید نقشه‌ای از پروژه‌ها ایجاد کنید که در آن هر پیام Project با یک کلید رشته مرتبط است، می‌توانید آن را به صورت زیر تعریف کنید:

map<string, Project> projects = 3;

  • فیلدهای نقشه را نمی توان repeated کرد.
  • ترتیب قالب سیمی و ترتیب تکرار نقشه مقادیر نقشه تعریف نشده است، بنابراین نمی توانید به ترتیب خاصی که موارد نقشه خود را دارند تکیه کنید.
  • هنگام ایجاد فرمت متن برای یک .proto ، نقشه ها بر اساس کلید مرتب می شوند. کلیدهای عددی به صورت عددی مرتب شده اند.
  • هنگام تجزیه از سیم یا هنگام ادغام، اگر کلیدهای نقشه تکراری وجود داشته باشد، آخرین کلید دیده شده استفاده می شود. هنگام تجزیه نقشه از قالب متن، در صورت وجود کلیدهای تکراری، تجزیه ممکن است با شکست مواجه شود.
  • اگر برای یک فیلد نقشه کلیدی ارائه کنید اما مقداری نداشته باشید، رفتار زمانی که فیلد سریالی است، وابسته به زبان است. در C++، جاوا، کاتلین و پایتون، مقدار پیش‌فرض نوع، سریال‌سازی می‌شود، در حالی که در زبان‌های دیگر هیچ چیز سریالی نیست.

API نقشه تولید شده در حال حاضر برای همه زبان های پشتیبانی شده proto3 در دسترس است. می‌توانید درباره نقشه API زبان انتخابی خود در مرجع API مربوطه اطلاعات بیشتری کسب کنید.

سازگاری با عقب

نحو نقشه معادل شکل زیر در سیم است، بنابراین اجرای بافرهای پروتکل که از نقشه ها پشتیبانی نمی کنند همچنان می توانند داده های شما را مدیریت کنند:

message MapFieldEntry {
  key_type key = 1;
  value_type value = 2;
}

repeated MapFieldEntry map_field = N;

هر پیاده سازی بافر پروتکل که از نقشه ها پشتیبانی می کند باید داده هایی را تولید کند و بپذیرد که با تعریف بالا قابل قبول باشد.

بسته ها

برای جلوگیری از تداخل نام بین انواع پیام پروتکل، می توانید یک مشخص کننده package اختیاری را به فایل .proto اضافه کنید.

package foo.bar;
message Open { ... }

سپس می توانید هنگام تعریف فیلدهای نوع پیام خود از مشخص کننده بسته استفاده کنید:

message Foo {
  ...
  foo.bar.Open open = 1;
  ...
}

روشی که یک مشخص کننده بسته بر روی کد تولید شده تأثیر می گذارد به زبان انتخابی شما بستگی دارد:

  • در C++ کلاس های تولید شده در فضای نام C++ پیچیده می شوند. برای مثال، Open در فضای نام foo::bar خواهد بود.
  • در جاوا و کاتلین ، بسته به عنوان بسته جاوا استفاده می شود، مگر اینکه به صراحت option java_package را در فایل .proto خود ارائه دهید.
  • در پایتون ، دستورالعمل بسته نادیده گرفته می شود، زیرا ماژول های پایتون بر اساس مکانشان در سیستم فایل سازماندهی می شوند.
  • در Go ، بسته به عنوان نام بسته Go استفاده می شود، مگر اینکه به صراحت option go_package را در فایل .proto خود ارائه دهید.
  • در Ruby ، کلاس‌های تولید شده درون فضاهای نام روبی تودرتو قرار می‌گیرند، و به سبک مورد نیاز بزرگ‌نویسی Ruby تبدیل می‌شوند (حرف اول با حروف بزرگ، اگر کاراکتر اول یک حرف نباشد، PB_ از قبل اضافه می‌شود). برای مثال، Open در فضای نام Foo::Bar خواهد بود.
  • در سی شارپ از بسته به عنوان فضای نام پس از تبدیل به PascalCase استفاده می شود، مگر اینکه شما به صراحت option csharp_namespace را در فایل .proto خود ارائه دهید. برای مثال، Open در فضای نام Foo.Bar خواهد بود.

بسته ها و وضوح نام

وضوح نام تایپ در زبان بافر پروتکل مانند C++ عمل می‌کند: ابتدا درونی‌ترین محدوده جستجو می‌شود، سپس درونی‌ترین دامنه، و به همین ترتیب، با در نظر گرفتن هر بسته به عنوان "داخلی" بسته والد خود. پیشرو '.' (به عنوان مثال، .foo.bar.Baz ) به معنای شروع از بیرونی ترین محدوده است.

کامپایلر بافر پروتکل همه نام‌های نوع را با تجزیه فایل‌های .proto وارد شده حل می‌کند. مولد کد برای هر زبان می داند که چگونه به هر نوع در آن زبان اشاره کند، حتی اگر قوانین محدوده متفاوتی داشته باشد.

تعریف خدمات

اگر می‌خواهید از انواع پیام خود با سیستم RPC (تماس رویه از راه دور) استفاده کنید، می‌توانید یک رابط سرویس RPC در یک فایل .proto کنید و کامپایلر بافر پروتکل کد رابط سرویس و مقالات خرد را به زبان انتخابی شما تولید می‌کند. بنابراین، برای مثال، اگر می خواهید یک سرویس RPC را با روشی تعریف کنید که SearchResponse SearchRequest گرداند، می توانید آن را در فایل .proto خود به صورت زیر تعریف کنید:

service SearchService {
  rpc Search(SearchRequest) returns (SearchResponse);
}

ساده ترین سیستم RPC برای استفاده با بافرهای پروتکل، gRPC است: یک سیستم منبع باز RPC بی طرف از زبان و پلت فرم که در گوگل توسعه یافته است. gRPC به ویژه با بافرهای پروتکل خوب کار می کند و به شما امکان می دهد کد RPC مربوطه را مستقیماً از فایل های .proto خود با استفاده از یک افزونه کامپایلر بافر پروتکل خاص تولید کنید.

اگر نمی‌خواهید از gRPC استفاده کنید، می‌توانید از بافرهای پروتکل با پیاده‌سازی RPC خودتان نیز استفاده کنید. می توانید در راهنمای زبان Proto2 اطلاعات بیشتری در مورد این موضوع پیدا کنید.

همچنین تعدادی پروژه شخص ثالث در حال انجام برای توسعه پیاده سازی RPC برای بافرهای پروتکل وجود دارد. برای لیستی از پیوندها به پروژه هایی که در مورد آنها می دانیم، به صفحه ویکی افزونه های شخص ثالث مراجعه کنید .

نقشه برداری JSON

Proto3 از یک رمزگذاری متعارف در JSON پشتیبانی می کند و به اشتراک گذاری داده ها بین سیستم ها را آسان تر می کند. کدگذاری بر اساس نوع به نوع در جدول زیر توضیح داده شده است.

هنگام تجزیه داده‌های کدگذاری‌شده با JSON در بافر پروتکل، اگر مقداری گم شده باشد یا مقدار آن null ، به عنوان مقدار پیش‌فرض مربوطه تفسیر می‌شود.

هنگام تولید خروجی با کد JSON از بافر پروتکل، اگر یک فیلد پروتوباف دارای مقدار پیش‌فرض باشد و اگر فیلد از حضور فیلد پشتیبانی نکند، به‌طور پیش‌فرض از خروجی حذف می‌شود. An implementation may provide options to include fields with default values in the output.

A proto3 field that is defined with the optional keyword supports field presence. Fields that have a value set and that support field presence always include the field value in the JSON-encoded output, even if it is the default value.

proto3 JSON JSON example Notes
message object {"fooBar": v, "g": null, …} Generates JSON objects. Message field names are mapped to lowerCamelCase and become JSON object keys. If the json_name field option is specified, the specified value will be used as the key instead. Parsers accept both the lowerCamelCase name (or the one specified by the json_name option) and the original proto field name. null is an accepted value for all field types and treated as the default value of the corresponding field type.
enum string "FOO_BAR" The name of the enum value as specified in proto is used. Parsers accept both enum names and integer values.
map<K,V> object {"k": v, …} All keys are converted to strings.
repeated V array [v, …] null is accepted as the empty list [] .
bool true, false true, false
string string "Hello World!"
bytes base64 string "YWJjMTIzIT8kKiYoKSctPUB+" JSON value will be the data encoded as a string using standard base64 encoding with paddings. Either standard or URL-safe base64 encoding with/without paddings are accepted.
int32, fixed32, uint32 number 1, -10, 0 JSON value will be a decimal number. Either numbers or strings are accepted.
int64, fixed64, uint64 string "1", "-10" JSON value will be a decimal string. Either numbers or strings are accepted.
float, double number 1.1, -10.0, 0, "NaN", "Infinity" JSON value will be a number or one of the special string values "NaN", "Infinity", and "-Infinity". Either numbers or strings are accepted. Exponent notation is also accepted. -0 is considered equivalent to 0.
Any object {"@type": "url", "f": v, … } If the Any contains a value that has a special JSON mapping, it will be converted as follows: {"@type": xxx, "value": yyy} . Otherwise, the value will be converted into a JSON object, and the "@type" field will be inserted to indicate the actual data type.
Timestamp string "1972-01-01T10:00:20.021Z" Uses RFC 3339, where generated output will always be Z-normalized and uses 0, 3, 6 or 9 fractional digits. Offsets other than "Z" are also accepted.
Duration string "1.000340012s", "1s" Generated output always contains 0, 3, 6, or 9 fractional digits, depending on required precision, followed by the suffix "s". Accepted are any fractional digits (also none) as long as they fit into nano-seconds precision and the suffix "s" is required.
Struct object { … } Any JSON object. See struct.proto .
Wrapper types various types 2, "2", "foo", true, "true", null, 0, … Wrappers use the same representation in JSON as the wrapped primitive type, except that null is allowed and preserved during data conversion and transfer.
FieldMask string "f.fooBar,h" See field_mask.proto .
ListValue array [foo, bar, …]
Value value Any JSON value. Check google.protobuf.Value for details.
NullValue null JSON null
Empty object {} An empty JSON object

JSON options

A proto3 JSON implementation may provide the following options:

  • Emit fields with default values : Fields with default values are omitted by default in proto3 JSON output. An implementation may provide an option to override this behavior and output fields with their default values.
  • Ignore unknown fields : Proto3 JSON parser should reject unknown fields by default but may provide an option to ignore unknown fields in parsing.
  • Use proto field name instead of lowerCamelCase name : By default proto3 JSON printer should convert the field name to lowerCamelCase and use that as the JSON name. An implementation may provide an option to use proto field name as the JSON name instead. Proto3 JSON parsers are required to accept both the converted lowerCamelCase name and the proto field name.
  • Emit enum values as integers instead of strings : The name of an enum value is used by default in JSON output. An option may be provided to use the numeric value of the enum value instead.

Options

Individual declarations in a .proto file can be annotated with a number of options . Options do not change the overall meaning of a declaration, but may affect the way it is handled in a particular context. The complete list of available options is defined in /google/protobuf/descriptor.proto .

Some options are file-level options, meaning they should be written at the top-level scope, not inside any message, enum, or service definition. Some options are message-level options, meaning they should be written inside message definitions. Some options are field-level options, meaning they should be written inside field definitions. Options can also be written on enum types, enum values, oneof fields, service types, and service methods; however, no useful options currently exist for any of these.

Here are a few of the most commonly used options:

  • java_package (file option): The package you want to use for your generated Java/Kotlin classes. If no explicit java_package option is given in the .proto file, then by default the proto package (specified using the "package" keyword in the .proto file) will be used. However, proto packages generally do not make good Java packages since proto packages are not expected to start with reverse domain names. If not generating Java or Kotlin code, this option has no effect.

    option java_package = "com.example.foo";
    
  • java_outer_classname (file option): The class name (and hence the file name) for the wrapper Java class you want to generate. If no explicit java_outer_classname is specified in the .proto file, the class name will be constructed by converting the .proto file name to camel-case (so foo_bar.proto becomes FooBar.java ). If the java_multiple_files option is disabled, then all other classes/enums/etc. generated for the .proto file will be generated within this outer wrapper Java class as nested classes/enums/etc. If not generating Java code, this option has no effect.

    option java_outer_classname = "Ponycopter";
    
  • java_multiple_files (file option): If false, only a single .java file will be generated for this .proto file, and all the Java classes/enums/etc. generated for the top-level messages, services, and enumerations will be nested inside of an outer class (see java_outer_classname ). If true, separate .java files will be generated for each of the Java classes/enums/etc. generated for the top-level messages, services, and enumerations, and the wrapper Java class generated for this .proto file won't contain any nested classes/enums/etc. This is a Boolean option which defaults to false . If not generating Java code, this option has no effect.

    option java_multiple_files = true;
    
  • optimize_for (file option): Can be set to SPEED , CODE_SIZE , or LITE_RUNTIME . This affects the C++ and Java code generators (and possibly third-party generators) in the following ways:

    • SPEED (default): The protocol buffer compiler will generate code for serializing, parsing, and performing other common operations on your message types. This code is highly optimized.
    • CODE_SIZE : The protocol buffer compiler will generate minimal classes and will rely on shared, reflection-based code to implement serialialization, parsing, and various other operations. The generated code will thus be much smaller than with SPEED , but operations will be slower. Classes will still implement exactly the same public API as they do in SPEED mode. This mode is most useful in apps that contain a very large number of .proto files and do not need all of them to be blindingly fast.
    • LITE_RUNTIME : The protocol buffer compiler will generate classes that depend only on the "lite" runtime library ( libprotobuf-lite instead of libprotobuf ). The lite runtime is much smaller than the full library (around an order of magnitude smaller) but omits certain features like descriptors and reflection. This is particularly useful for apps running on constrained platforms like mobile phones. The compiler will still generate fast implementations of all methods as it does in SPEED mode. Generated classes will only implement the MessageLite interface in each language, which provides only a subset of the methods of the full Message interface.
    option optimize_for = CODE_SIZE;
    
  • cc_enable_arenas (file option): Enables arena allocation for C++ generated code.

  • objc_class_prefix (file option): Sets the Objective-C class prefix which is prepended to all Objective-C generated classes and enums from this .proto. There is no default. You should use prefixes that are between 3-5 uppercase characters as recommended by Apple . Note that all 2 letter prefixes are reserved by Apple.

  • deprecated (field option): If set to true , indicates that the field is deprecated and should not be used by new code. In most languages this has no actual effect. In Java, this becomes a @Deprecated annotation. For C++, clang-tidy will generate warnings whenever deprecated fields are used. In the future, other language-specific code generators may generate deprecation annotations on the field's accessors, which will in turn cause a warning to be emitted when compiling code which attempts to use the field. If the field is not used by anyone and you want to prevent new users from using it, consider replacing the field declaration with a reserved statement.

    int32 old_field = 6 [deprecated = true];
    

Custom Options

Protocol Buffers also allows you to define and use your own options. This is an advanced feature which most people don't need. If you do think you need to create your own options, see the Proto2 Language Guide for details. Note that creating custom options uses extensions , which are permitted only for custom options in proto3.

Generating Your Classes

To generate the Java, Kotlin, Python, C++, Go, Ruby, Objective-C, or C# code you need to work with the message types defined in a .proto file, you need to run the protocol buffer compiler protoc on the .proto . If you haven't installed the compiler, download the package and follow the instructions in the README. For Go, you also need to install a special code generator plugin for the compiler: you can find this and installation instructions in the golang/protobuf repository on GitHub.

The Protocol Compiler is invoked as follows:

protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR --go_out=DST_DIR --ruby_out=DST_DIR --objc_out=DST_DIR --csharp_out=DST_DIR path/to/file.proto
  • IMPORT_PATH specifies a directory in which to look for .proto files when resolving import directives. If omitted, the current directory is used. Multiple import directories can be specified by passing the --proto_path option multiple times; they will be searched in order. -I=_IMPORT_PATH_ can be used as a short form of --proto_path .
  • You can provide one or more output directives :

    As an extra convenience, if the DST_DIR ends in .zip or .jar , the compiler will write the output to a single ZIP-format archive file with the given name. .jar outputs will also be given a manifest file as required by the Java JAR specification. Note that if the output archive already exists, it will be overwritten; the compiler is not smart enough to add files to an existing archive.

  • You must provide one or more .proto files as input. Multiple .proto files can be specified at once. Although the files are named relative to the current directory, each file must reside in one of the IMPORT_PATH s so that the compiler can determine its canonical name.

File location

Prefer not to put .proto files in the same directory as other language sources. Consider creating a subpackage proto for .proto files, under the root package for your project.

Location Should be Language-agnostic

When working with Java code, it's handy to put related .proto files in the same directory as the Java source. However, if any non-Java code ever uses the same protos, the path prefix will no longer make sense. So in general, put the protos in a related language-agnostic directory such as //myteam/mypackage .

The exception to this rule is when it's clear that the protos will be used only in a Java context, such as for testing.

Supported Platforms

For information about: