Working with protobuf types

  • The Google Ads API uses proto3 as its default payload format, requiring an understanding of its conventions and types.

  • Optional fields in proto3 allow distinguishing between fields with empty values and fields not sent by the server, providing methods to clear or check if a field is set.

  • Repeated fields are represented as readonly RepeatedField<T> in the .NET client library and implement the IList<T> interface, which can be populated using the AddRange method or collection initializer syntax.

  • Oneof fields can hold different types but only one value at a time, and in the .NET library, they are implemented with properties sharing a shared class field, where one assignment can overwrite a previous one.

  • Proto3 objects can be converted to JSON format for interfacing with systems requiring text-based data or serialized to bytes for more memory and storage efficiency.

Since the Google Ads API uses proto3 as its default payload format, it's important to understand a few Protocol Buffers conventions and types when working with the .NET client library.

Optional fields

Many fields in the Google Ads API are marked as optional. This lets you distinguish between cases where the field has an empty value versus when the server does not return a value for the field. These fields behave like regular properties, except they also provide additional methods to clear the field and to check if the field is set.

For example, the Name field of the Campaign object is marked as optional, so you can use the following methods to work with this field:

// 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 the Name field from
// being sent to the server in a subsequent API call.
campaign.ClearName();

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

// This throws a runtime ArgumentNullException. Use ClearName() instead.
campaign.Name = null;

Repeated fields

A field array is represented in the Google Ads API as a read-only RepeatedField.

For example, a campaign's url_custom_parameters field is a repeated field, so it's represented as a read-only RepeatedField<CustomParameter> in the .NET client library. RepeatedField<T> implements the IList<T> interface.

There are two ways to populate a RepeatedField property:

AddRange method

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

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

Collection initializer syntax

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

Oneof fields

Some fields in the Google Ads API are marked as oneof fields, meaning that the field can hold different types but only one value at a given time. oneof fields are similar to a union type in the C programming language.

The .NET library implements oneof fields by providing one property for each type of value that can be held in a oneof field, with all the properties updating a shared underlying storage field.

For example, the campaign's campaign_bidding_strategy is marked as a oneof field. This class is implemented as follows (code simplified for brevity):

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

Since oneof properties share storage, one assignment can overwrite a previous assignment, leading to subtle bugs. For example:

Campaign campaign = new Campaign()
{
    ManualCpc = new ManualCpc(),
    ManualCpm = new ManualCpm()
};

In this case, campaign.ManualCpc is now null because initializing the campaign.ManualCpm property overwrites the previous initialization for campaign.ManualCpc.

Conversion to other formats

Convert to JSON format

You can convert protobuf objects to JSON format and back using Google.Protobuf.JsonFormatter. This is useful when building systems that need to interface with other systems requiring text-based formats like JSON or XML.

using Google.Protobuf;

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

Convert to bytes

You can serialize an object to bytes and back using Google.Protobuf. Binary serialization is more memory- and storage-efficient than JSON format.

using Google.Protobuf;

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