Créer un flux de menu

Dans le tutoriel suivant, nous allons vous montrer comment créer un flux au format JSON à l'aide de la définition des tampons de protocole Google (protobuf). Nous allons utiliser le compilateur de tampon de protocole pour générer le code source basé sur le schéma de tampon de protocole. La création d'un flux à l'aide du code source généré offre une interface simple et évite que des entités de flux soient créées avec des noms ou types de champs incorrects.

Configuration du projet

  • Créez un répertoire de projet.
  • Copiez le contenu de food_menu.protomoney.proto localized_text.proto des définitions proto en tant que nouveaux fichiers dans la racine du répertoire de votre projet.
  • Installer le compilateur protoc

    Pour générer le code source à partir des fichiers .proto, vous aurez besoin du compilateur de protoc. Téléchargez le dernier binaire précompilé sur la page des versions GitHub de Protocol Buffers.

    Extrayez le fichier ZIP et ajoutez le chemin d'accès bin à la variable d'environnement PATH, par exemple:

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

Générer le code source

Python

  1. Installez la bibliothèque protobuf Python
    pip install protobuf
        
  2. Générer le code source du client

    Créer le répertoire de sortie proto : "generate"

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

    Mettez à jour la variable d'environnement PYTHONPATH pour inclure le chemin généré, par exemple:

    export PYTHONPATH="$PYTHONPATH:./generated"
        

    Si vous utilisez Bazel, essayez la règle py_proto_library comme alternative à l'exécution de protoc.

Exemple d'utilisation

Pour consulter un exemple de projet complet, cliquez ici.

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

    

L'exemple de code montre comment créer un objet de flux avec chacune des entités suivantes:

  • Menu
  • MenuSection
  • MenuItem
  • MenuItemOption

L'exemple montre ensuite comment sérialiser le flux au format JSON.

L'API Python permet l'initialisation différée des objets imbriqués en définissant les propriétés.

TypeScript

  1. Installez le plug-in protoc TypeScript.
    npm init
    npm i -D typescript
    npm i ts-proto
          
    Notez que ts-proto n'est pas un projet Google faisant l'objet d'une assistance officielle.
  2. Créez le répertoire de sortie et générez le code source du client.

    Créez le répertoire de sortie 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
          

    Si vous utilisez Bazel, essayez la règle js_proto_library comme alternative à l'exécution de protoc.

Exemple d'utilisation

Pour consulter un exemple de projet complet, cliquez ici.

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

  

L'exemple de code montre comment créer un objet de flux avec chacune des entités suivantes:

  • Menu
  • MenuSection
  • MenuItem
  • MenuItemOption

L'exemple montre ensuite comment sérialiser le flux au format JSON.

Java

  1. Ajoutez les dépendances protobuf-java et protobuf-java à votre projet à l'aide de Maven ou Gradle comme décrit ici.
  2. Générer le code source du client
    protoc --java_out=src/main/java food_menu.proto money.proto localized_text.proto
        

    Si vous utilisez Maven, vous pouvez utiliser le protobuf-maven-plugin pour générer le code source au moment de la compilation.

    Si vous utilisez Bazel, essayez la règle java_proto_library comme alternative à l'exécution de protoc.

Exemple d'utilisation

Pour consulter un exemple de projet complet, cliquez ici.

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

    

L'exemple de code montre comment créer un objet de flux avec chacune des entités suivantes:

  • Menu
  • MenuSection
  • MenuItem
  • MenuItemOption

L'exemple montre ensuite comment sérialiser le flux au format JSON.

Go

  1. Installer le plug-in protoc pour go
    go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
        

    Mettez à jour la variable d'environnement PATH pour inclure le plug-in protoc-gen-go, par exemple:

    export PATH="$PATH:$(go env GOPATH)/bin"
        
  2. Initialisez l'application et générez le code source du client.
    go mod init feed/app
    mkdir generated
    protoc --go_out=./generated/ food_menu.proto money.proto localized_text.proto
        

    Si vous utilisez Bazel, essayez la règle go_proto_library au lieu d'exécuter protoc.

Exemple d'utilisation

Pour consulter un exemple de projet complet, cliquez ici.

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


    

L'exemple de code montre comment créer un objet de flux avec chacune des entités suivantes:

  • Menu
  • MenuSection
  • MenuItem
  • MenuItemOption

L'exemple montre ensuite comment sérialiser le flux au format JSON.