Como trabalhar com tipos Protobuf

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

Campos opcionais

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

Por exemplo, o campo Name do objeto Campaign é marcado como opcional. Portanto, você pode 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 campo é representada na API Google Ads como uma RepeatedField somente leitura.

Um exemplo é um campo url_custom_parameters de uma campanha sendo um campo repetido. Portanto, ele é 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 de RepeatedField.

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

Veja 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 C# mais recentes: usar a sintaxe do inicializador da 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 OneOf, o que significa que o campo pode 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 campos OneOf fornecendo uma propriedade para cada tipo de valor que pode ser mantido em um campo OneOf, além de 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 o 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 de campaign.ManualCpc.

Conversão para outros formatos

É fácil converter objetos protobuf para 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);

Também é possível serializar um objeto para bytes e vice-versa. A serialização binária é mais eficiente em termos de 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);