چگونه یک فید منو بسازیم

در آموزش زیر نحوه ساخت فید در JSON با استفاده از تعریف بافرهای پروتکل Google (protobuf) را نشان خواهیم داد. ما از کامپایلر protobuf برای تولید کد منبع بر اساس طرح پروتوباف استفاده خواهیم کرد. ساختن فید با استفاده از کد منبع تولید شده، رابط کاربری آسانی را فراهم می کند و از ایجاد موجودیت های فید با نام فیلدها یا انواع فیلدهای نادرست جلوگیری می کند.

راه اندازی پروژه

  • یک فهرست پروژه جدید ایجاد کنید.
  • محتویات food_menu.proto money.proto localized_text.proto را از تعاریف پروتو به عنوان فایل های جدید در ریشه دایرکتوری پروژه خود کپی کنید.
  • کامپایلر پروتک را نصب کنید

    برای تولید کد منبع از فایل‌های .proto، به کامپایلر پروتک نیاز دارید. آخرین باینری از پیش ساخته شده را از صفحه انتشار Protocol Buffers GitHub دانلود کنید.

    zip را استخراج کرده و مسیر bin را به متغیر محیطی PATH اضافه کنید، برای مثال:

    unzip protoc-22.0-linux-x86_64.zip -d /usr/local/protoc
    export PATH="$PATH:/usr/local/protoc/bin"
      

کد منبع تولید کنید

پایتون

  1. کتابخانه Python protobuf
    pip install protobuf
        
    نصب کنید
  2. کد منبع مشتری را ایجاد کنید.

    دایرکتوری خروجی پروتو ایجاد کنید: تولید شده است

    protoc --python_out=./generated food_menu.proto money.proto localized_text.proto
        

    متغیر محیطی PYTHONPATH را به‌روزرسانی کنید تا مسیر تولید شده را برای مثال شامل شود:

    export PYTHONPATH="$PYTHONPATH:./generated"
        

    اگر از Bazel استفاده می کنید، قانون py_proto_library را به عنوان جایگزینی برای اجرای پروتک امتحان کنید.

مثال استفاده

نمونه کامل پروژه را می توانید در اینجا پیدا کنید.

"""Menu feed example used in

https://developers.google.com/maps-booking/verticals/dining/guides/tutorials/tutorial-menu-feed-protos#python.
"""
import json
from generated import food_menu_pb2
from google.protobuf.json_format import MessageToDict

# create feed
feed = food_menu_pb2.FoodMenuFeed()

# add a menu component to feed data
menuComponent = feed.data.add()
menuComponent.menu.menu_id = 'menu1'
menuComponent.menu.merchant_ids.append('dining-1')
menuDisplayName = menuComponent.menu.display_name.text.add()
menuDisplayName.text = 'Menu'
menuDisplayName.language_code = 'en-us'
menuComponent.menu.language = 'en-us'
menuComponent.menu.last_merchant_update_time.seconds = 1633621547

for i in ['appetizers', 'dinner']:
  menuComponent.menu.menu_section_ids.append(i)

# add a menu section component to feed data
sectionComponent = feed.data.add()
sectionComponent.section.menu_section_id = 'appetizers'
sectionDisplayName = sectionComponent.section.display_name.text.add()
sectionDisplayName.text = 'Lunch Appetizers'
sectionDisplayName.language_code = 'en-us'
sectionComponent.section.menu_item_ids.append('breadsticks-sauce')

# add a menu item component to feed data
itemComponent = feed.data.add()
itemComponent.item.menu_item_id = 'breadsticks-sauce'
itemDisplayName = itemComponent.item.display_name.text.add()
itemDisplayName.text = 'Breadsticks & Sauce'
itemDisplayName.language_code = 'en-us'
itemDescription = itemComponent.item.description.text.add()
itemDescription.text = 'Breakfast basket w/ side of tomato sauce (size 6 or 12)'
itemDescription.language_code = 'en-us'

for i in ['breadstick-sm', 'breadstick-lg']:
  itemComponent.item.menu_item_option_set.menu_item_option_ids.append(i)

for i in [
    'http://www.example.com/photos/breadsticks.jpg',
    'http://www.example.com/photos/sauce.jpg',
]:
  itemImage = itemComponent.item.images.add()
  itemImage.uri = i

# add a menu item option component to feed data
optionComponent = feed.data.add()
optionComponent.option.menu_item_option_id: 'breadstick-sm'
optionComponent.option.value.property_type = (
    food_menu_pb2.MenuItemOptionProperty.PropertyType.SIZE
)
optionTextValue = optionComponent.option.value.text_val.text.add()
optionTextValue.text = 'Small'
optionTextValue.language_code = 'en-us'
optionOffer = optionComponent.option.offer_set.offers.add()
optionOffer.price.currency_code = 'USD'
optionOffer.price.units = 8
optionOffer.price.nanos = 0

feedJSON = json.dumps(MessageToDict(feed, preserving_proto_field_name=True))
print(feedJSON)

    

مثال کد نحوه ایجاد یک شیء فید را با یکی از هر یک از موجودیت های زیر نشان می دهد:

  • منو
  • بخش منو
  • آیتم منو
  • MenuItemOption

سپس این مثال نحوه سریال‌سازی فید را به JSON نشان می‌دهد.

رابط برنامه‌نویسی پایتون با تنظیم ویژگی‌ها، مقدار اولیه‌سازی تنبلی اشیاء تودرتو را امکان‌پذیر می‌کند.

TypeScript

  1. افزونه پروتکل TypeScript را نصب کنید.
    npm init
    npm i -D typescript
    npm i ts-proto
          
    توجه داشته باشید که ts-proto یک پروژه رسمی Google نیست.
  2. دایرکتوری خروجی را ایجاد کنید و کد منبع مشتری را تولید کنید.

    دایرکتوری خروجی پروتو ایجاد کنید: src/generated

    protoc --plugin="./node_modules/.bin/protoc-gen-ts_proto" --ts_proto_opt=useOptionals=all --ts_proto_opt=snakeToCamel=false --ts_proto_opt=onlyTypes=true --ts_proto_out="./src/generated" food_menu.proto money.proto localized_text.proto
          

    اگر از Bazel استفاده می کنید، قانون js_proto_library را به عنوان جایگزینی برای اجرای پروتک امتحان کنید.

مثال استفاده

نمونه کامل پروژه را می توانید در اینجا پیدا کنید.

import {FoodMenuFeed, Menu, MenuItem, MenuSection, MenuItemOption, MenuItemOptionProperty_PropertyType} from './generated/food_menu';

const menu: Menu = {
  menu_id: 'menu1',
  merchant_ids: ['dining-1'],
  display_name: {text: [{text: 'Menu', language_code: 'en-us'}]},
  language: 'en-us',
  menu_section_ids: ['appetizers', 'dinner'],
  last_merchant_update_time: new Date()
};

const section: MenuSection = {
  menu_section_id: 'appetizers',
  display_name: {text: [{text: 'Lunch Appetizers', language_code: 'en-us'}]},
  menu_section_ids: ['breadsticks-sauce']
};

const item: MenuItem = {
  menu_item_id: 'breadsticks-sauce',
  display_name: {text: [{text: 'Breadsticks & Sauce', language_code: 'en-us'}]},
  description: {
    text: [{
      text: 'Breakfast basket w/ side of tomato sauce (size 6 or 12)',
      language_code: 'en-us'
    }]
  },
  menu_item_option_set:
      {menu_item_option_ids: ['breadstick-sm', 'breadstick-lg']},
  images: [
    {uri: 'http://www.example.com/photos/breadsticks.jpg'},
    {uri: 'http://www.example.com/photos/sauce.jpg'}
  ]
};

const option: MenuItemOption = {
  menu_item_option_id: 'breadstick-sm',
  value: {
    property_type: MenuItemOptionProperty_PropertyType.SIZE,
    text_val: {
      text: [
        {text: "Small", language_code: "en-us"}
      ]
     }
  },
  offer_set: {
    offers: [{
      price: {
        currency_code: "USD",
        units: 8,
        nanos: 0
      }
    }]
  }
};

const feed: FoodMenuFeed = {
  data: [{menu}, {section}, {item}, {option}]
};

console.log(JSON.stringify(feed));

  

مثال کد نحوه ایجاد یک شیء فید را با یکی از هر یک از موجودیت های زیر نشان می دهد:

  • منو
  • بخش منو
  • آیتم منو
  • MenuItemOption

سپس این مثال نحوه سریال‌سازی فید را به JSON نشان می‌دهد.

جاوا

  1. وابستگی‌های protobuf-java و protobuf-java-util را با استفاده از maven یا gradle به پروژه خود اضافه کنید.
  2. کد منبع مشتری را ایجاد کنید.
    protoc --java_out=src/main/java food_menu.proto money.proto localized_text.proto
        

    هنگام استفاده از maven، می‌توانید از افزونه protobuf-maven برای تولید کد منبع در زمان کامپایل استفاده کنید.

    اگر از Bazel استفاده می کنید، قانون java_proto_library را به عنوان جایگزینی برای اجرای پروتک امتحان کنید.

مثال استفاده

نمونه کامل پروژه را می توانید در اینجا پیدا کنید.

package com.example;

import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Timestamp;
import com.google.type.Money;
import com.google.protobuf.util.JsonFormat;
import com.google.type.LocalizedText;
import food.menu.v1.FoodMenu.FoodMenuFeed;
import food.menu.v1.FoodMenu.Menu;
import food.menu.v1.FoodMenu.MenuComponent;
import food.menu.v1.FoodMenu.MenuItem;
import food.menu.v1.FoodMenu.MenuSection;
import food.menu.v1.FoodMenu.TextField;
import food.menu.v1.FoodMenu.MenuItemOption;
import food.menu.v1.FoodMenu.MenuItemOptionProperty;
import food.menu.v1.FoodMenu.OfferSet;
import food.menu.v1.FoodMenu.Offer;

/**
 * Menu feed example used in
 *
 * <p>https://developers.google.com/maps-booking/verticals/dining/guides/tutorials/tutorial-menu-feed-protos#java.
 */
public class Feed {

  public static void main(String[] args) throws InvalidProtocolBufferException {
    Feed feed = new Feed();
    feed.createMenuFeed();
  }

  public void createMenuFeed() throws InvalidProtocolBufferException {

    Menu.Builder menu =
        Menu.newBuilder()
            .setMenuId("menu1")
            .addMerchantIds("dining-1")
            .setDisplayName(
                TextField.newBuilder()
                    .addText(LocalizedText.newBuilder().setText("Menu").setLanguageCode("en-us")))
            .setLanguage("en-us")
            .setLastMerchantUpdateTime(Timestamp.newBuilder().setSeconds(1633621547));

    MenuSection.Builder section =
        MenuSection.newBuilder()
            .setMenuSectionId("appetizers")
            .setDisplayName(
                TextField.newBuilder()
                    .addText(
                        LocalizedText.newBuilder()
                            .setText("Lunch Appetizers")
                            .setLanguageCode("en-us")))
            .addMenuItemIds("breadsticks-sauce");

    MenuItem.Builder item =
        MenuItem.newBuilder()
            .setMenuItemId("breadsticks-sauce")
            .setDisplayName(
                TextField.newBuilder()
                    .addText(
                        LocalizedText.newBuilder()
                            .setText("Breadsticks & Sauce")
                            .setLanguageCode("en-us")))
            .setDescription(
                TextField.newBuilder()
                    .addText(
                        LocalizedText.newBuilder()
                            .setText("Breadsticks & Sauce")
                            .setLanguageCode("en-us")));

    MenuItemOption.Builder option =
        MenuItemOption.newBuilder()
          .setMenuItemOptionId("breadstick-sm")
          .setValue(
              MenuItemOptionProperty.newBuilder()
                .setPropertyType(MenuItemOptionProperty.PropertyType.SIZE)
                .setTextVal(TextField.newBuilder()
                    .addText(
                        LocalizedText.newBuilder()
                            .setText("Small")
                            .setLanguageCode("en-us"))))
          .setOfferSet(
              OfferSet.newBuilder()
                .addOffers(
                    Offer.newBuilder()
                      .setPrice(
                          Money.newBuilder()
                            .setCurrencyCode("USD")
                            .setUnits(8)
                            .setNanos(0))));

    FoodMenuFeed.Builder foodMenuFeed =
        FoodMenuFeed.newBuilder()
            .addData(MenuComponent.newBuilder().setMenu(menu))
            .addData(MenuComponent.newBuilder().setSection(section))
            .addData(MenuComponent.newBuilder().setItem(item))
            .addData(MenuComponent.newBuilder().setOption(option));

    String feedJSON =
        JsonFormat.printer()
            .omittingInsignificantWhitespace()
            .preservingProtoFieldNames()
            .print(foodMenuFeed);

    System.out.println(feedJSON);
  }
}

    

مثال کد نحوه ایجاد یک شیء فید را با یکی از هر یک از موجودیت های زیر نشان می دهد:

  • منو
  • بخش منو
  • آیتم منو
  • MenuItemOption

سپس این مثال نحوه سریال‌سازی فید را به JSON نشان می‌دهد.

برو

  1. افزونه protoc را برای go
    go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
        
    نصب کنید

    متغیر محیطی PATH را به‌روزرسانی کنید تا افزونه protoc-gen-go را نیز شامل شود، برای مثال:

    export PATH="$PATH:$(go env GOPATH)/bin"
        
  2. برنامه را راه اندازی کنید و کد منبع مشتری را تولید کنید.
    go mod init feed/app
    mkdir generated
    protoc --go_out=./generated/ food_menu.proto money.proto localized_text.proto
        

    اگر از Bazel استفاده می کنید، قانون go_proto_library را به عنوان جایگزینی برای اجرای پروتک امتحان کنید.

مثال استفاده

نمونه کامل پروژه را می توانید در اینجا پیدا کنید.

/*
Menu feed example used in
https://developers.google.com/maps-booking/verticals/dining/guides/tutorials/tutorial-menu-feed-protos#go.
*
*/
package main

import (
	pb "feed/app/generated/food/menu/v1/proto"
	"fmt"

	localized_text "google.golang.org/genproto/googleapis/type/localized_text"
	money "google.golang.org/genproto/googleapis/type/money"
	"google.golang.org/protobuf/encoding/protojson"
	timestamppb "google.golang.org/protobuf/types/known/timestamppb"
)

func main() {

	//create a menu component
	menu := &pb.Menu{
		MenuId:      "menu1",
		MerchantIds: []string{"dining-1"},
		DisplayName: &pb.TextField{
			Text: []*localized_text.LocalizedText{{
				Text:         "Menu",
				LanguageCode: "en-us",
			}},
		},
		Language: "en-us",
		LastMerchantUpdateTime: &timestamppb.Timestamp{
			Seconds: 1633621547,
		},
	}

	//create a menu section component
	section := &pb.MenuSection{
		MenuSectionId: "appetizers",
		DisplayName: &pb.TextField{
			Text: []*localized_text.LocalizedText{{
				Text:         "Lunch Appetizers",
				LanguageCode: "en-us",
			}},
		},
		MenuItemIds: []string{"breadsticks-sauce"},
	}

	//create a menu item component
	item := &pb.MenuItem{
		MenuItemId: "breadsticks-sauce",
		DisplayName: &pb.TextField{
			Text: []*localized_text.LocalizedText{{
				Text:         "Breadsticks & Sauce",
				LanguageCode: "en-us",
			}},
		},
		Description: &pb.TextField{
			Text: []*localized_text.LocalizedText{{
				Text:         "Breakfast basket w/ side of tomato sauce (size 6 or 12)",
				LanguageCode: "en-us",
			}},
		},
		Pricing: &pb.MenuItem_MenuItemOptionSet_{
			MenuItemOptionSet: &pb.MenuItem_MenuItemOptionSet{
				MenuItemOptionIds: []string{"breadstick-sm", "breadstick-lg"},
			},
		},
	}
	imageUris := []string{
		"http://www.example.com/photos/breadsticks.jpg",
		"http://www.example.com/photos/sauce.jpg",
	}
	for _, uri := range imageUris {
		image := &pb.Image{
			Uri: uri,
		}
		item.Images = append(item.Images, image)
	}

	//create a menu item option
	option := &pb.MenuItemOption{
		MenuItemOptionId: "breadstick-sm",
		Value: &pb.MenuItemOptionProperty{
			PropertyType: pb.MenuItemOptionProperty_SIZE,
			Value: &pb.MenuItemOptionProperty_TextVal{
				TextVal: &pb.TextField{
					Text: []*localized_text.LocalizedText{{
						Text:         "Small",
						LanguageCode: "en-us",
					}},
				},
			},
		},
		OfferSet: &pb.OfferSet{
			Offers: []*pb.Offer{{
				Price: &money.Money{
					CurrencyCode: "USD",
					Units:        8,
					Nanos:        0,
				},
			}},
		},
	}

	//create feed
	feed := &pb.FoodMenuFeed{
		Data: []*pb.MenuComponent{{
			Type: &pb.MenuComponent_Menu{
				Menu: menu,
			},
		},
			{
				Type: &pb.MenuComponent_Section{
					Section: section,
				},
			},
			{
				Type: &pb.MenuComponent_Item{
					Item: item,
				},
			},
			{
				Type: &pb.MenuComponent_Option{
					Option: option,
				},
			}},
	}

	marshalOptions := protojson.MarshalOptions{
		UseProtoNames: true,
	}
	jsonBytes, _ := marshalOptions.Marshal(feed)
	fmt.Printf("message = %s", string(jsonBytes))
}


    

مثال کد نحوه ایجاد یک شیء فید را با یکی از هر یک از موجودیت های زیر نشان می دهد:

  • منو
  • بخش منو
  • آیتم منو
  • MenuItemOption

سپس این مثال نحوه سریال‌سازی فید را به JSON نشان می‌دهد.