Google APIs Client Library for C++ | A C++ library for client applications to access Google APIs. |
This document discusses the JSON abstractions and implementations used by the client library for Data Objects in generated Service APIs.
Contents
Basic Concepts
The core client library interfaces refer to application data objects using
the abstract class googleapis::client::SerializableJson
.
This class is so abstract that it only knows how to serialize and
deserialize the instance in and out of
JSON. In theory this
makes the core client library independent of the JSON implementation.
The library provides a concrete implementation of this class with a
ready-to-use interface in googleapis::client::JsonCppData
.
The data objects emitted by the code generator are derived from this
class and provide a specialized interface for the class as defined by
the specification in the service's Discovery Document. Because the abstract
class is so generic, there isn't much to say about it so this document
focuses on the JsonCppData
implementation provided with the SDK.
See the section The JsonCppData base class
later in this document for more information about JsonCppData
.
Overview of Data Types used in the Data Models
JSON data objects are only used in the service-specific APIs. These data objects use the Primitive Types and String Types discussed among the Foundation Types. In addition, the code generator will use:
Type | Description | |||||||
---|---|---|---|---|---|---|---|---|
[u]int(16|32|64)
| For JSON integer values. The JSON format determines the
specific integer variant. The SDK defines all these in
googleapis/base/integral_types.h .
Note that JSON actually represents 64-bit integers as strings
with the format '[u]int64' however the APIs will use native
int64 for their public interface.
| |||||||
bool
| For JSON boolean values. | |||||||
float | double
| For JSON number values. | |||||||
string
| For JSON string values with 'byte' or no format. | |||||||
googleapis::client::DateTime
googleapis::client::JsonCppData
For JSON 'object' or 'any' values.
It is also the base class for custom typed Data Objects.
| googleapis::client::JsonCppDataArray
For JSON 'array' objects. A | JsonCppDataArray
is a subclass of JsonCppData . That is, it is itself
a JsonCppData object.
These arrays also provide a C++ iterator .
googleapis::client::JsonCppAssociativeArray
For JSON 'map' objects.
A | JsonCppDataAssociativeArray
is a subclass of JsonCppData . That is, it is itself
a JsonCppData object.
These maps also provide a C++ iterator .
template<typename T>
googleapis::client::JsonCppDataCapsule;
|
A capsule is a |
Typical Use Case Examples
Simple Structures
#include "googleapis/base/integral_types.h" #include "googleapis/client/util/date_time.h" #include "googleapis/client/data/jsoncpp_data.h" #include "google/drive_api/drive_api.h" using google_drive_api::File; using googleapis::client::JsonCppData; using googleapis::client::JsonCppDataCapsule; using googleapis::client::DateTime; void ProcessData(const JsonCppData& data); void InitFileData(File* file) { DateTime now; int64 size = 123; // just to illustrate using int64 const StringPiece kText = "some sample tokens"; // for illustrative purposes. file->set_createdDate(now); // Date-Time file->set_description("Sample File"); // string file->set_editable(true); // boolean file->set_fileSize(size); // int64 (string with int64 format) File::FileIndexableText& indexable_text = file->mutable_indexableText(); indexable_text.set_text(kText); // This is just showing that changing the attribute obtained from a container // takes effect on the container itself. assert(indexable_test == file->indexableText().get_text()); } void UseLocalData() { JsonCppDataCapsule<File> file; InitFileData(&file); ProcessData(file); } void UseDataFactory() { scoped_ptr<File> file(File::New()); InitFileData(file.get()); ProcessData(*file); }
Dates and Times
#include "googleapis/client/util/date_time.h" using googleapis::client::DateTime; void Example() { DateTime now; // Default constructor is current time // The following are all equivalent time_t epoch = now.ToEpochTime(); DateTime from_epoch(epoch); struct tm utc; now.GetUniversalTime(&utc); DateTime from_utc(DateTime::DateTimeFromLocal(utc)); struct tm local; now.GetLocalTime(&local); DateTime from_local(DateTime::DateTimeFromLocal(local)); struct timeval tv; now.GetTimeval(&tv); DateTime from_timeval(tv); string str = now.ToString(); DateTime from_str(str); }
Arrays
#include "googleapis/client/data/jsoncpp_data.h" using googleapis::client::JsonCppArray; using googleapis::client::JsonCppData; class MyData : public JsonCppData { public: int get_number() const; void set_number(int n); }; void ExampleArray() { JsonCppCapsule<JsonCppArray<int> > primitive_array; JsonCppCapsule<JsonCppArray<MyData> > obj_array; for (int i = 0; i < 10; ++i) { primitive_array.set(i, 10 * i); // cannot set with [] operator obj_array[i].set_number(10 * i); // can use either [] or set_ } for (int i = 0; i < 10; ++i) { // reading primitives can use [] operator assert(primitive_array[i] == obj_array[i].get_number()); } // C++11 style iteration for (int i: primitive_array) { } // Tranditional iterator for (auto it = obj_array.begin(); it != obj_array.end(); +it) { int index = it.index(); const MyData& data = *it; } // Can import/export to stl vectors. vector<int> v; primitive_array.Export(&v); primtive_array.Import(v); }
Observe that non-object array types do not have a mutable []
operator. They only have an immutable accessor. You must use the array's
set_
method. To read values from the array you can always use
either the []
operator or get
method.
Associative Arrays
#include "googleapis/client/data/jsoncpp_data.h" using googleapis::client::JsonCppAssociativeArray; void ExampleAssociativeArray() { JsonCppCapsule<JsonCppAssociativeArray<int> > aa; aa_put("one", 1); aa_put("two", 2); int value; if (aa_get("three", &value)) { } for (auto it = aa.begin(); it != aa.end(); ++it) { cout << it.key() << " = " << it.value(); } for (const auto& elem : aa) { cout << elem.key() << " = " << elem.value(); } }
JsonCppData Reference
This section provides some additional reference documentation for using
the JsonCppData
class. All data objects generated by the
code generator are derived from this class. At some point you may need
to become more familiar with the details.
The JsonCppData base class
JsonCppData
is defined in
googleapis/client/data/jsoncpp_data.h
JsonCppData
is implemented using the external
open source JsonCpp library
for convienence, with a more standard C++ API on top of it
so that the specialized data objects generated for service-specific APIs
look and feel more like C++. The resulting class has some quirks due to
limitations in the underlying JsonCpp library interface for this usage
pattern but client code using it can be written using a normal style.
JsonCppData
is always a wrapper around an exernal
Json::Value
instance implemented by the JsonCpp library.
Under normal circumstances, this is completely hidden from you. However
if you are already using the JsonCpp library, or need direct access for
some reason, the JsonCppData
can provide direct access to the
Json::Value
instance.
The base JsonCppData
stores all its state in the
Json::Value
it was constructed with. It merely delegates
to that storage to manage its attributes. Specialized subclasses, such
as data objects for service-specific APIs, provide wrapper functions
to facilitate this and provide a more C++-like API.
#include "base/scoped_ptr.h" #include "googleapis/client/data/jsoncpp_data.h" using googleapis::client::JsonCppData; using googleapis::client::JsonCppDataCapsule; class MyData : public JsonCppData { public: explicit MyData(const Json::Value& storage) : JsonCppData(storage) {} explicit MyData(Json::Value* storage) : JsonCppData(storage) {} int get_number() const { return Storage("number").asInt(); } void set_number(int n) { *MutableStorage("number") = n; } const MyData next() const { return MyData(Storage("next")); } MyData next_mutable() { return MyData(MutableStorage("next")); } static MyData* New() { return new JsonCppDataCapsule<MyData>; } }; void SampleUsage() { scoped_ptr<MyData> data(MyData::New()); data->set_number(1); data->next_mutable().set_number(2); data->next_mutable().next_mutable().set_number(3); for (MyData elem = *data; !elem.IsNull(); elem = elem.next()) { cout << elem.get_number(); } }
JsonCppData
instances are inherently const or mutable
independent of language const
decorators. This is determined
at construction time. If the instance is constructed with a
const Json::Value&
reference then it will be immutable. If it is
constructed with a Json::Value*
pointer it will be mutable.
The API uses const
decorators as any other class would to
use normal C++ compiler protection. However the objects are self-enforcing
at runtime. This means even if you cast away the language const
at compile time, the runtime will know and not let you perform the action.
This behavior is an anomaly specific to JsonCppData
as a
solution to some limitations in the underlying JsonCpp library
which does not directly lend itself to the client libraries usage pattern.
Json::Value storage; MyData immutable_data(storage); MyData mutable_data(&storage); assert(mutable_data.IsMutable() == true); assert(immutable_data.IsMutable() == false); Json::Value* ptr = mutable_data.MutableStorage(); // ok // The following will CHECK fail as an illegal operation. ptr = immutable_data.MutableStorage();
The API is designed such that as long as you preserve constness then
everything will work properly. But you const_cast
away
constness then the library will catch you.
The proper way to have constructed immutable_data
in the
previous example would be:
const
MyData immutable_data(storage);
That way the compiler would have caught the invalid call to
immutable_data.MutableStorage()
because of the call
from a const
instance to a non-const
method.