Cách xây dựng nguồn cấp dữ liệu thực đơn

Trong hướng dẫn sau, chúng tôi sẽ minh hoạ cách tạo nguồn cấp dữ liệu trong JSON bằng cách sử dụng định nghĩa Vùng đệm giao thức của Google (protobuf). Chúng tôi sẽ sử dụng trình biên dịch protobuf để tạo mã nguồn dựa trên giản đồ protobuf. Việc tạo nguồn cấp dữ liệu bằng mã nguồn đã tạo mang đến một giao diện dễ dàng và ngăn việc tạo các thực thể nguồn cấp dữ liệu bằng tên trường hoặc loại trường không chính xác.

Thiết lập dự án

  • Tạo thư mục dự án mới.
  • Sao chép nội dung của food_menu.proto amount.proto local_text.proto trong định nghĩa proto dưới dạng tệp mới vào gốc thư mục dự án của bạn.
  • Cài đặt trình biên dịch protoc

    Để tạo mã nguồn từ tệp .proto, bạn cần có trình biên dịch protoc. Tải tệp nhị phân tạo sẵn mới nhất xuống từ trang phát hành Protocol Buffers GitHub.

    Trích xuất tệp zip và thêm đường dẫn bin vào biến môi trường PATH, ví dụ:

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

Tạo mã nguồn

Python

  1. Cài đặt thư viện Python protobuf
    pip install protobuf
        
  2. Tạo mã nguồn ứng dụng.

    Tạo thư mục đầu ra proto: đã tạo

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

    Cập nhật biến môi trường PYTHONPATH để bao gồm đường dẫn đã tạo, ví dụ:

    export PYTHONPATH="$PYTHONPATH:./generated"
        

    Nếu bạn đang dùng Bazel, hãy thử quy tắc py_proto_library để thay thế cho việc chạy protoc.

Ví dụ về cách sử dụng

Bạn có thể xem toàn bộ dự án mẫu tại đây.

"""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)

    

Ví dụ về mã minh hoạ cách tạo đối tượng nguồn cấp dữ liệu bằng một trong các thực thể sau:

  • Trình đơn
  • MenuSection
  • MenuItem
  • MenuItemOption

Sau đó là ví dụ về cách chuyển đổi tuần tự nguồn cấp dữ liệu thành JSON.

API Python cho phép khởi tạo từng phần các đối tượng lồng nhau bằng cách đặt thuộc tính.

TypeScript

  1. Cài đặt trình bổ trợ protoc TypeScript.
    npm init
    npm i -D typescript
    npm i ts-proto
          
    Xin lưu ý rằng ts-proto không phải là một dự án được Google hỗ trợ chính thức.
  2. Tạo thư mục đầu ra và tạo mã nguồn ứng dụng.

    Tạo thư mục đầu ra proto: src/created

    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
          

    Nếu bạn đang dùng Bazel, hãy thử quy tắc js_proto_library để thay thế cho việc chạy protoc.

Ví dụ về cách sử dụng

Bạn có thể xem toàn bộ dự án mẫu tại đây.

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));

  

Ví dụ về mã minh hoạ cách tạo đối tượng nguồn cấp dữ liệu bằng một trong các thực thể sau:

  • Trình đơn
  • MenuSection
  • MenuItem
  • MenuItemOption

Sau đó là ví dụ về cách chuyển đổi tuần tự nguồn cấp dữ liệu thành JSON.

Java

  1. Thêm các phần phụ thuộc protobuf-javaprotobuf-java vào dự án bằng maven hoặc gradle như mô tả tại đây.
  2. Tạo mã nguồn ứng dụng.
    protoc --java_out=src/main/java food_menu.proto money.proto localized_text.proto
        

    Bạn có thể sử dụng protobuf-maven-plugin để tạo mã nguồn trong thời gian biên dịch khi sử dụng maven.

    Nếu bạn đang dùng Bazel, hãy thử quy tắc java_proto_library để thay thế cho việc chạy protoc.

Ví dụ về cách sử dụng

Bạn có thể xem toàn bộ dự án mẫu tại đây.

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);
  }
}

    

Ví dụ về mã minh hoạ cách tạo đối tượng nguồn cấp dữ liệu bằng một trong các thực thể sau:

  • Trình đơn
  • MenuSection
  • MenuItem
  • MenuItemOption

Sau đó là ví dụ về cách chuyển đổi tuần tự nguồn cấp dữ liệu thành JSON.

Go

  1. Cài đặt trình bổ trợ protoc cho go
    go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
        

    Cập nhật biến môi trường PATH để thêm trình bổ trợ protoc-gen-go, ví dụ:

    export PATH="$PATH:$(go env GOPATH)/bin"
        
  2. Khởi động ứng dụng và tạo mã nguồn ứng dụng.
    go mod init feed/app
    mkdir generated
    protoc --go_out=./generated/ food_menu.proto money.proto localized_text.proto
        

    Nếu bạn đang dùng Bazel, hãy thử quy tắc go_proto_library để thay thế cho việc chạy protoc.

Ví dụ về cách sử dụng

Bạn có thể xem toàn bộ dự án mẫu tại đây.

/*
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))
}


    

Ví dụ về mã minh hoạ cách tạo đối tượng nguồn cấp dữ liệu bằng một trong các thực thể sau:

  • Trình đơn
  • MenuSection
  • MenuItem
  • MenuItemOption

Sau đó là ví dụ về cách chuyển đổi tuần tự nguồn cấp dữ liệu thành JSON.