TinyORM: Serialization
- Introduction
- Serializing Models & Collections
- Hiding Attributes From JSON
- Appending Values To JSON
- Date Serialization
Introduction​
Stability: 2 - Stable
When building APIs using TinyORM, you will often need to convert your models and relationships to vectors, maps, or JSON. TinyORM includes convenient methods for making these conversions, as well as controlling which attributes are included in the serialized representation of your models.
Serializing Models & Collections​
Serializing To Vectors & Maps​
To convert a model and its loaded relationships to a vector, you should use the toList
or toMap
methods. This methods are recursive, so all attributes and all relations (including the relations of relations) will be converted to vectors:
using Models::User;
auto user = User::with("roles")->first();
return user->toList();
return user->toMap();
The attributesToList
or attributesToMap
methods may be used to convert a model's attributes to a vector or map but not its relationships:
auto user = User::first();
return user->attributesToList();
return user->attributesToMap();
You may also convert entire collections of models to vectors or maps by calling the toList
or toMap
methods on the collection instance:
ModelsCollection<User> users = User::with("roles")->all();
return users.toList();
return users.toMap();
Serializing To JSON​
To convert a model to JSON, you should use the toJson
method. Like toList
or toMap
, the toJson
method is recursive, so all attributes and relations will be converted to JSON. You may also specify any JSON encoding options that are supported by QJsonDocument::toJson:
using Models::User;
auto user = User::with("roles")->find(1);
return user->toJson();
return user->toJson(QJsonDocument::Indented);
You may also convert entire collections of models to JSON by calling the toJson
method on the collection instance:
ModelsCollection<User> users = User::with("roles")->findMany({1, 2});
return users.toJson();
You can also convert models to the QJsonObject
and QJsonDocument
using the toJsonArray
and toJsonDocument
methods and collection of models to QJsonArray
and QJsonDocument
using the toJsonArray
and toJsonDocument
methods.
Relationships​
When a TinyORM model is converted to JSON, its loaded relationships will automatically be included as attributes on the JSON object. Also, though TinyORM relationship methods are defined using "camelCase" method names, a relationship's JSON attributes will be "snake_case".
This behavior is affected and can be overridden by the u_snakeAttributes
static data member:
#include <orm/tiny/model.hpp>
using Orm::Tiny::Model;
class Album final : public Model<Album, AlbumImage>
{
friend Model;
using Model::Model;
/*! Indicates whether attributes are snake_cased during serialization. */
inline static const bool u_snakeAttributes = false;
};
Hiding Attributes From JSON​
Sometimes you may wish to limit the attributes, such as passwords, that are included in your model's vector, map, or JSON representation. To do so, add a u_hidden
static data member to your model. Attributes that are listed in the u_hidden
data member set will not be included in the serialized representation of your model:
#include <orm/tiny/model.hpp>
using Orm::Tiny::Model;
class User final : public Model<User>
{
friend Model;
using Model::Model;
/*! The attributes that should be hidden during serialization. */
inline static std::set<QString> u_hidden {"password"};
};
To hide relationships, add the relationship's method name to your TinyORM model's u_hidden
static data member.
Alternatively, you may use the u_visible
static data member to define an "allow list" of attributes that should be included in your model's vector, map, and JSON representation. All attributes that are not present in the u_visible
set will be hidden during serialization:
#include <orm/tiny/model.hpp>
using Orm::Tiny::Model;
class User final : public Model<User>
{
friend Model;
using Model::Model;
/*! The attributes that should be visible during serialization. */
inline static std::set<QString> u_visible {
"first_name", "last_name",
};
};
Temporarily Modifying Attribute Visibility​
If you would like to make some typically hidden attributes visible on a given model instance, you may use the makeVisible
method. The makeVisible
method returns a model reference:
return user.makeVisible("attribute").toMap();
return user.makeVisible({"id", "name"}).toMap();
Likewise, if you would like to hide some attributes that are typically visible, you may use the makeHidden
method.
return user.makeHidden("attribute").toList();
return user.makeHidden({"id", "name"}).toList();
If you wish to temporarily override all of the visible or hidden attributes, you may use the setVisible
and setHidden
methods respectively:
return user.setVisible({"id", "name"}).toMap();
return user.setHidden({"email", "password", "note"}).toJson();
You can also clear all visible and hidden attributes or determine whether a visible / hidden attribute is defined:
user.clearVisible();
user.clearHidden();
return user.hasVisible("name");
return user.hasHidden("password");
Appending Values To JSON​
Occasionally, when converting models to vector, map, or JSON, you may wish to add attributes that do not have a corresponding column in your database. To do so, first define an accessor for the value:
#include <orm/tiny/model.hpp>
using Orm::Tiny::Model;
class User final : public Model<User>
{
friend Model;
using Model::Model;
protected:
/*! Accessor to determine if the user is an administrator. */
Attribute isAdmin() const noexcept
{
return Attribute::make(/* get */ []() -> QVariant
{
return QStringLiteral("yes");
});
}
};
If you would like the accessor to always be appended to your model's vector, map, and JSON representations, you may add the attribute name to the u_appends
data member set of your model. Note that attribute names are typically referenced using their "snake_case" serialized representation, even though the accessor's method name is defined using "camelCase":
#include <orm/tiny/model.hpp>
using Orm::Tiny::Model;
class User final : public Model<User>
{
friend Model;
using Model::Model;
/*! Map of mutator names to methods. */
inline static const QHash<QString, MutatorFunction> u_mutators {
{"is_admin", &User::isAdmin},
};
/*! The attributes that should be appended during serialization. */
std::set<QString> u_appends {"is_admin"};
};
Once the attribute has been added to the u_appends
set, it will be included in both the model's vector, map, and JSON representations. Attributes in the u_appends
set will also respect the u_visible
and u_hidden
attribute settings configured on the model.
Special note should be given to the u_mutators
static data member map, which maps accessors' attribute names to its methods. This data member is required because C++ does not currently support reflection.
Appending At Run Time​
At runtime, you may instruct a model instance to append additional attributes using the append
method. Or, you may use the setAppends
method to override the entire set of appended attributes for a given model instance:
return user.append("is_admin").toList();
return user.append({"is_admin", "is_banned"}).toMap();
return user.setAppends({"is_admin"}).toJson();
And you can also clear all appends or determine whether an append is defined:
user.clearAppends();
return user.hasAppend("is_admin");
Date Serialization​
Customizing The Default Date Format​
You may customize the default serialization format by overriding the serializeDate
and serializeDateTime
methods. These methods do not affect how your dates are formatted for storage in the database:
/*! Prepare a date for vector, map, or JSON serialization. */
QString serializeDate(const QDate date)
{
return date.toString("yyyy-MM-dd");
}
/*! Prepare a datetime for vector, map, or JSON serialization. */
QString serializeDateTime(const QDateTime &datetime)
{
return datetime.toUTC().toString("yyyy-MM-ddTHH:mm:ssZ");
}
Customizing The Date Format Per Attribute​
You may customize the serialization format of individual TinyORM date attributes by specifying the date format in the model's cast declarations:
/*! The attributes that should be cast. */
inline static std::unordered_map<QString, CastItem> u_casts {
{"birthday", {CastType::CustomQDate, "yyyy-MM-dd"}},
{"joined_at", {CastType::CustomQDateTime, "yyyy-MM-dd HH:00"}},
};