- تعریف یک نوع پیام
- انواع ارزش اسکالر
- مقادیر پیش فرض
- شمارش ها
- استفاده از انواع پیام های دیگر
- انواع تو در تو
- به روز رسانی یک نوع پیام
- فیلدهای ناشناخته
- هر
- یکی از
- نقشه ها
- بسته ها
- تعریف خدمات
- نقشه برداری JSON
- گزینه ها
- ایجاد کلاس های شما
- محل
- پلتفرم های پشتیبانی شده
این راهنما نحوه استفاده از زبان بافر پروتکل برای ساختار دادههای بافر پروتکل، از جمله نحو فایل .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
سازگار است (توجه داشته باشید که اگر مقادیر مناسب نباشند، کوتاه می شوند). با این حال، توجه داشته باشید که کد مشتری ممکن است با آنها رفتار متفاوتی داشته باشد، زمانی که پیام از سریال خارج میشود: برای مثال، انواع proto3enum
ناشناخته در پیام حفظ میشوند، اما نحوه نمایش آن در صورت عدم سریالسازی پیام به زبان بستگی دارد. فیلدهای 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 explicitjava_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 explicitjava_outer_classname
is specified in the.proto
file, the class name will be constructed by converting the.proto
file name to camel-case (sofoo_bar.proto
becomesFooBar.java
). If thejava_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 (seejava_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 tofalse
. If not generating Java code, this option has no effect.option java_multiple_files = true;
optimize_for
(file option): Can be set toSPEED
,CODE_SIZE
, orLITE_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 withSPEED
, but operations will be slower. Classes will still implement exactly the same public API as they do inSPEED
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 oflibprotobuf
). 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 inSPEED
mode. Generated classes will only implement theMessageLite
interface in each language, which provides only a subset of the methods of the fullMessage
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 totrue
, 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 resolvingimport
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 :
-
--cpp_out
generates C++ code inDST_DIR
. See the C++ generated code reference for more. -
--java_out
generates Java code inDST_DIR
. See the Java generated code reference for more. -
--kotlin_out
generates additional Kotlin code inDST_DIR
. See the Kotlin generated code reference for more. -
--python_out
generates Python code inDST_DIR
. See the Python generated code reference for more. -
--go_out
generates Go code inDST_DIR
. See the Go generated code reference for more. -
--ruby_out
generates Ruby code inDST_DIR
. See the Ruby generated code reference for more. -
--objc_out
generates Objective-C code inDST_DIR
. See the Objective-C generated code reference for more. -
--csharp_out
generates C# code inDST_DIR
. See the C# generated code reference for more. -
--php_out
generates PHP code inDST_DIR
. See the PHP generated code reference for more.
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 theIMPORT_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:
- the operating systems, compilers, build systems, and C++ versions that are supported, see Foundational C++ Support Policy .
- the PHP versions that are supported, see Supported PHP versions .