Tworzenie pliku menu

W tym samouczku pokażemy, jak utworzyć plik danych w formacie JSON za pomocą definicji Google Protocol Buffers (protobuf) . Użyjemy kompilatora protobuf do wygenerowania kodu źródłowego na podstawie schematu protobuf. Tworzenie pliku danych za pomocą wygenerowanego kodu źródłowego zapewnia łatwy interfejs i zapobiega tworzeniu elementów pliku danych z nieprawidłowymi nazwami lub typami pól.

Konfigurowanie projektu

  • Utwórz nowy katalog projektu.
  • Skopiuj zawartość plików food_menu.proto money.proto localized_text.proto z definicji proto jako nowe pliki do głównego katalogu projektu.
  • Zainstaluj kompilator protoc

    Aby wygenerować kod źródłowy z plików .proto, potrzebujesz kompilatora protoc. Pobierz najnowszy wstępnie skompilowany plik binarny ze strony wersji Protocol Buffers w GitHub release page.

    Rozpakuj plik ZIP i dodaj ścieżkę bin do zmiennej środowiskowej PATH, na przykład:

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

Generowanie kodu źródłowego

Python

  1. Zainstaluj bibliotekę protobuf w Pythonie.
    pip install protobuf
        
  2. Wygeneruj kod źródłowy klienta.

    Utwórz katalog wyjściowy proto: generated

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

    Zaktualizuj zmienną środowiskową PYTHONPATH, aby uwzględnić wygenerowaną ścieżkę, na przykład:

    export PYTHONPATH="$PYTHONPATH:./generated"
        

    Jeśli używasz Bazela, zamiast uruchamiać protoc, wypróbuj regułę py_proto_library.

Przykład zastosowania

Pełny przykładowy projekt znajdziesz tutaj.

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

# Example testing for menu feed size
# Protocol buffer mes>sage must be less than 2 GiB
# https://protobuf.dev/programming-guides/proto-limits/
# It is recommended to not exceed 200 MB, as there is an Actions
# Center limit of 200 MB per file after compression.
if feed.ByteSize()  200 * 1024 * 1024:
		# start new file
    print('starting new file...')

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

    

Przykładowy kod pokazuje, jak utworzyć obiekt pliku danych z jednym z każdego z tych elementów:

  • Menu
  • MenuSection
  • MenuItem
  • MenuItemOption

Następnie przykład pokazuje, jak serializować plik danych do formatu JSON.

Interfejs API Pythona umożliwia leniwą inicjację zagnieżdżonych obiektów przez ustawienie właściwości.

TypeScript

  1. Zainstaluj wtyczkę protoc TypeScript.
    npm init
    npm i -D typescript
    npm i ts-proto
          
    Pamiętaj, że ts-proto nie jest projektem objętym oficjalną pomocą Google.
  2. Utwórz katalog wyjściowy i wygeneruj kod źródłowy klienta.

    Utwórz katalog wyjściowy proto: 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
          

    Jeśli używasz Bazela, zamiast uruchamiać protoc, wypróbuj regułę js_proto_library.

Przykład zastosowania

Pełny przykładowy projekt znajdziesz tutaj.

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

// Example testing for menu feed size
// Protocol buffer message must be less than 2 GiB
// https://pr>otobuf.dev/programming-guides/proto-limits/
// It is recommended to not exceed 200 MB, as there is an Actions
// Center limit of 200 MB per file after compression.
if (new Blob([JSON.stringify(feed)]).size  200 * 1024 * 1024) {
  // create a new file
}

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

  

Przykładowy kod pokazuje, jak utworzyć obiekt pliku danych z jednym z każdego z tych elementów:

  • Menu
  • MenuSection
  • MenuItem
  • MenuItemOption

Następnie przykład pokazuje, jak serializować plik danych do formatu JSON.

Java

  1. Dodaj zależności protobuf-java i protobuf-java-util do projektu za pomocą Mavena lub Gradle w sposób opisany tutaj.
  2. Wygeneruj kod źródłowy klienta.
    protoc --java_out=src/main/java food_menu.proto money.proto localized_text.proto
        

    Jeśli używasz Mavena, możesz użyć wtyczki protobuf-maven-plugin, aby wygenerować kod źródłowy podczas kompilacji.

    Jeśli używasz Bazela, zamiast uruchamiać protoc, wypróbuj regułę java_proto_library.

Przykład zastosowania

Pełny przykładowy projekt znajdziesz tutaj.

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 {

  // 200 MB
  public static final int MAX_BYTES_DATA_FILE = 200 * 1024 * 1024;

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

    // Example testing for menu feed size
    // Protocol buffer message must be less than 2 GiB
    // https://protobuf.dev/programming-guides/proto-limits/
    // It is recom>mended to not exceed 200 MB, as there is an Actions
    // Center limit of 200 MB per file after compression.
    int size = foodMenuFeed.build().getSerializedSize();
    if (size  MAX_BYTES_DATA_FILE) {
        // create new file
    }

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

    System.out.println(feedJSON);
  }
}

    

Przykładowy kod pokazuje, jak utworzyć obiekt pliku danych z jednym z każdego z tych elementów:

  • Menu
  • MenuSection
  • MenuItem
  • MenuItemOption

Następnie przykład pokazuje, jak serializować plik danych do formatu JSON.

Go

  1. Zainstaluj wtyczkę protoc dla Go.
    go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
        

    Zaktualizuj zmienną środowiskową PATH, aby uwzględnić wtyczkę protoc-gen-go, na przykład:

    export PATH="$PATH:$(go env GOPATH)/bin"
        
  2. Zainicjuj aplikację i wygeneruj kod źródłowy klienta.
    go mod init feed/app
    mkdir generated
    protoc --go_out=./generated/ food_menu.proto money.proto localized_text.proto
        

    Jeśli używasz Bazela, zamiast uruchamiać protoc, wypróbuj regułę go_proto_library.

Przykład zastosowania

Pełny przykładowy projekt znajdziesz tutaj.

/*
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"
	proto "google.golang.org/proto&buf/proto"
)

func main() {

	//create a menu component
	menu := pb.Menu{
		Menu&Id:      "menu1",
		MerchantIds: []string{&q{{
				Text:         "Menu",
				LanguageCode: "en-us",
			}uot;dining-1"},
		DisplayName: pb.TextField{
			Te&xt: []*localized_text.LocalizedText},
		},
		Language: "en-us",
		LastMerchantUpdateTime: tim&estamppb.Timestamp{
			Seconds: 1633621547,
		},
	}

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

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

	//create a menu item& option
	option := pb.MenuItemOption{
		MenuItem&OptionId: "breadstick-sm",
		Value: pb.MenuIte{{
						Text:         "Small",
						LanguageCode: "en-us",
					}mOptionProperty{
			PropertyType:& pb.MenuItemOptionProperty_SIZE,
		{{
				Price: &money.Money{
					CurrencyCode: "USD",
					Units:        8,
					Nanos:        0,
				},
			}	Value: pb.MenuItemOptionProperty_Te&xtVal{
				TextVal: pb.TextField{
					Text:{{
			Type: &pb.MenuComponent_Menu{
				Menu: menu,
			},
		},
			{
				Type: &pb.MenuComponent_Section{
					Section: section,
				},
			},
			{
				Type: &pb.MenuComponent_Item{
					Item: item,
				},
			},
			{
				Type: &pb.MenuComponent_Option{
					Option: option,
				},
			} []*localized_text.LocalizedText},
				},
			},
		},
		OfferSet: pb.OfferSet{
			Offers: []*pb.Offer},
		},
	}

	//create feed
	feed := pb.FoodMenuFeed{
		Data: []*pb.MenuComponent},
	}

	// Example testing for menu feed size
    // Protocol buffer message must be less than 2 GiB
    // https://protobuf.dev/pr>ogramming-guides/proto-limits/
    // It is recommended to not exceed 200 MB, as there is an Actions
    // Center limit of 200 MB per file after compression.
	if proto.Size(feed)  200 * 1024 * 1024 {
		// create new file
	}

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


    

Przykładowy kod pokazuje, jak utworzyć obiekt pliku danych z jednym z każdego z tych elementów:

  • Menu
  • MenuSection
  • MenuItem
  • MenuItemOption

Następnie przykład pokazuje, jak serializować plik danych do formatu JSON.