Como trabalhar com tipos de Protobuf

Como a API Google Ads usa o Protobuf como formato de payload padrão, é importante entender algumas convenções e tipos de Protobuf ao trabalhar com a API.

Campos opcionais

Muitos campos na API Google Ads estão marcados como optional. Isso permite distinguir entre casos em que o campo tem um valor vazio e em que o servidor não enviou um valor para o campo. Esses campos se comportam como campos regulares, mas também fornecem outros métodos para limpar o campo e verificar se ele está definido.

Por exemplo, o campo Name do objeto Campaign é marcado como opcional. É possível usar os métodos a seguir para trabalhar com esse campo.

// Get the name.
string name = campaign.Name;

// Set the name.
campaign.Name = name;

// Check if the campaign object has the name field set.
bool hasName = campaign.HasName();

// Clear the name field. Use this method to exclude Name field from
// being sent to the server in a subsequent API call.
campaign.ClearName();

// Set the campaign to empty string value. This value will be
// sent to the server if you use this object in a subsequent API call.
campaign.Name = "";

// This will throw a runtime error. Use ClearName() instead.
campaign.Name = null;

Tipos repetidos

Uma matriz de campos é representada na API Google Ads como um RepeatedField somente leitura.

Um exemplo é o campo url_custom_parameters de uma campanha que é um campo repetido, por isso é representado como um RepeatedField<CustomParameter> somente leitura na biblioteca de cliente .NET.

O RepeatedField implementa a interface IList<T>.

Há duas maneiras de preencher um campo RepeatedField.

Versão C# mais antiga: adicionar valores usando o método AddRange

Confira um exemplo abaixo.

Campaign campaign = new Campaign()
{
    ResourceName = ResourceNames.Campaign(customerId, campaignId),
    Status = CampaignStatus.Paused,
};

// Add values to UrlCustomParameters using AddRange method.
campaign.UrlCustomParameters.AddRange(new CustomParameter[]
{
    new CustomParameter { Key = "season", Value = "christmas" },
    new CustomParameter { Key = "promocode", Value = "NY123" }
});

Versões mais recentes do C#: usar a sintaxe inicializadora de coleção

// Option 1: Initialize the field directly.
Campaign campaign = new Campaign()
{
    ResourceName = ResourceNames.Campaign(customerId, campaignId),
    Status = CampaignStatus.Paused,
    // Directly initialize the field.
    UrlCustomParameters =
    {
        new CustomParameter { Key = "season", Value = "christmas" },
        new CustomParameter { Key = "promocode", Value = "NY123" }
    }
};

// Option 2: Initialize using an intermediate variable.
CustomParameter[] parameters = new CustomParameter[]
{
    new CustomParameter { Key = "season", Value = "christmas" },
    new CustomParameter { Key = "promocode", Value = "NY123" }
}

Campaign campaign1 = new Campaign()
{
    ResourceName = ResourceNames.Campaign(customerId, campaignId),
    Status = CampaignStatus.Paused,
    // Initialize from an existing array.
    UrlCustomParameters = { parameters }
};

Tipos OneOf

Alguns campos na API Google Ads são marcados como campos OneOf. Isso significa que podem conter tipos diferentes, mas apenas um valor por vez. Os campos OneOf são semelhantes ao tipo de união em C.

A biblioteca .NET implementa os campos OneOf, fornecendo uma propriedade para cada tipo de valor que pode ser mantido em um campo OneOf, e todas as propriedades que atualizam um campo de classe compartilhada.

Por exemplo, o campaign_bidding_strategy da campanha é marcado como um campo OneOf. Essa classe é implementada da seguinte maneira (código simplificado para simplificar):

public sealed partial class Campaign : pb::IMessage<Campaign>
{
    object campaignBiddingStrategy_ = null;
    CampaignBiddingStrategyOneofCase campaignBiddingStrategyCase_;

    public ManualCpc ManualCpc
    {
        get
        {
            return campaignBiddingStrategyCase_ == CampaignBiddingStrategyOneofCase.ManualCpc ?
                (ManualCpc) campaignBiddingStrategy_ : null;
        }
        set
        {
            campaignBiddingStrategy_ = value;
            campaignBiddingStrategyCase_ = CampaignBiddingStrategyOneofCase.ManualCpc;
        }
    }

    public ManualCpm ManualCpm
    {
        get
        {
            return campaignBiddingStrategyCase_ == CampaignBiddingStrategyOneofCase.ManualCpm ?
                (ManualCpm) campaignBiddingStrategy_ : null;
        }
        set
        {
            campaignBiddingStrategy_ = value;
            campaignBiddingStrategyCase_ = CampaignBiddingStrategyOneofCase.ManualCpm;
        }
    }

    public CampaignBiddingStrategyOneofCase CampaignBiddingStrategyCase
    {
        get { return campaignBiddingStrategyCase_; }
    }
}

Como as propriedades OneOf compartilham armazenamento, uma atribuição pode substituir uma atribuição anterior, levando a bugs sutis. Por exemplo,

Campaign campaign = new Campaign()
{
    ManualCpc = new ManualCpc()
    {
        EnhancedCpcEnabled = true
    },
    ManualCpm = new ManualCpm()
    {

    }
};

Nesse caso, campaign.ManualCpc agora é null, já que a inicialização do campo campaign.ManualCpm substitui a inicialização anterior para campaign.ManualCpc.

Conversão para outros formatos

Você pode converter facilmente objetos protobuf para o formato JSON e vice-versa. Isso é útil ao criar sistemas que precisam interagir com outros sistemas que exigem dados em formatos baseados em texto, como JSON ou XML.

GoogleAdsRow row = new GoogleAdsRow()
{
    Campaign = new Campaign()
    {
        Id = 123,
        Name = "Campaign 1",
        ResourceName = ResourceNames.Campaign(1234567890, 123)
    }
};
// Serialize to JSON and back.
string json = JsonFormatter.Default.Format(row);
row = GoogleAdsRow.Parser.ParseJson(json);

Você também pode serializar um objeto em bytes e vice-versa. A serialização binária é mais eficiente em memória e armazenamento do que o formato JSON.

GoogleAdsRow row = new GoogleAdsRow()
{
    Campaign = new Campaign()
    {
        Id = 123,
        Name = "Campaign 1",
        ResourceName = ResourceNames.Campaign(1234567890, 123)
    }
};
// Serialize to bytes and back.
byte[] bytes = row.ToByteArray();
row = GoogleAdsRow.Parser.ParseFrom(bytes);