Awesome
Qt Json Autogen
A build-time tool that automatically generate implementation for C++ class serialization/deserialization to/from json.
If your project is based on Qt, this tool may help a lot.
Introduction
qasc
(Qt Auto Serialization Compiler) is a code generator based on moc
in Qt 5.15.2 source, it will generate the implementation of conversions between classes/enumerations and QJsonObject
/QString
.
In order to have as little association with the QObject framework as possible, qasc
implements the conversion between enumeration and string itself.
Enumeration
// Source
enum Direction {
__qas_attr__("west")
West,
East,
North,
__qas_exclude__
South,
}
QAS_JSON_NS(Direction)
int main() {
qDebug() << qAsEnumToJson(Direction::West);
qDebug() << qAsEnumToJson(Direction::East);
qDebug() << qAsEnumToJson(Direction::North);
qDebug() << qAsEnumToJson(Direction::South);
return 0;
}
# Output
"west"
"East"
"North"
""
-
QAS_JSON
tellsqasc
to generate the conversion implementations for classDirection
, it also declare two stream operator functions in current scope. If the current scope is a class, useQAS_JSON
instead, which will declare two friend functions. -
__qas_attr__("west")
tellsqasc
that the string form of enum valueWest
should bewest
. The string form of enum values without this annotation are the original token in your code. -
__qas_exclude__
tellsqasc
that enumSouth
won't be processed in conversion.
Class/Struct
// Source
class Class {
public:
class Student {
public:
enum Gender {
Male,
Female,
};
QAS_JSON(Gender);
QString name;
Gender gender;
int age;
};
QAS_JSON(Student);
__qas_attr__("Class")
QString className;
QMap<QString, Student> students; // id -> student
__qas_exclude__
QString otherInfo;
};
QAS_JSON_NS(Class)
int main() {
Class cls;
cls.className = "F114514";
cls.students = {
{"1", {"alice", Class::Student::Female, 18}},
{"2", {"bob", Class::Student::Male, 17}},
{"3", {"mark", Class::Student::Male, 19}},
};
cls.otherInfo = "PHP is the best programming language.";
qDebug().noquote() << QJsonDocument(qAsClassToJson(cls)).toJson();
return 0;
}
# Output
{
"Class": "F114514",
"students": {
"1": {
"age": 18,
"gender": "Female",
"name": "alice"
},
"2": {
"age": 17,
"gender": "Male",
"name": "bob"
},
"3": {
"age": 19,
"gender": "Male",
"name": "mark"
}
}
}
-
The usage of class is similar to enum.
-
Add a
__qas_attr__("XXX")
annotation before a declaration of a member to specify key when insert to a json object; -
By default, the public members will participate in serialization/deserialization while the protected or private members won't.
- Add a
__qas_exclude__
annotation before a public member to exclude it. - Add a
__qas_include__
annotation before a protected or private member to include it. When a non-public member partipates in serialization/deserialization, theQAS_JSON
declaration must be right in the scope of the class, because only friend functions can access the non-public members.class Sample { public: QString foo; private: __qas_include__ QString bar; QAS_JSON(Sample) // This declaration cannot be outside };
- Add a
-
For a derived class, all its public derived super classes will participate in serialization/deserialization while the protected or private ones won't.
- Add a
__qas_exclude__
annotation before a public super class name to exclude it. - Add a
__qas_include__
annotation before a protected or private super class name to include it. For same reason, you need to declareQAS_JSON
in the derived class scope.class Foo { public: int foo = 1; }; class Bar { public: int bar = 2; }; class Baz { public: int baz = 3; }; class Qux { public: int qux = 4; }; QAS_JSON_NS(Foo) QAS_JSON_NS(Bar) QAS_JSON_NS(Baz) QAS_JSON_NS(Qux) class Messed : public Foo, protected __qas_include__ Bar, public __qas_exclude__ Baz, protected Qux { public: QString token = "what"; QAS_JSON(Messed) };
- Add a
-
A class/struct can be serializable by
qasc
if- All its included super classes can be serializd into a json object
- All its included members meets the following conditions:
- A serializable class
- A supported container of serialzable classes
- List and map types in STL or Qt are supported
- Use macro defined in
qas_json_types.h
to register your own container
- A serializable enumeration
-
Otherwise you may need to use
QAS_JSON_IMPL
orQAS_JSON_NS_IMPL
to declare the functions(2QAS::JsonStream
's stream operators to be overloaded) and implement them yourself. -
The serializable class should have a parameterless constructor.
-
Initializing member variables in class definitions is not recommended, you should do it in constructor.
// Not recommended struct Foo { int a = 1; char b{}; };
-
So we can conclude that constant, reference, pointer members may need to be excluded.
-
Some useful util functions are provided in
qjsonstream.h
.
Supported Types
C++ Type | JSON Type |
---|---|
custom structures/classes | object |
bool | true/false |
signed and unsigned integral types | number |
float and double | number |
enum and enum class | string |
QString | string |
iteratable lists (QVector , QList , std::vector , std::list ) | array |
sets (QSet , std::set , std::unordered_set ) | array |
map (QMap , QHash , std::map , std::unordered_map ) | object |
How To Use
Add Into CMake Project
-
Example
CMakeLists.txt
find_package(qastool REQUIRED) include_directories(${QASTOOL_INCLUDE_DIRS}) set(_src ... ) # Add source files set(_headers_for_qas ...) # Headers using QAS macros # Specify your target, for example an exe add_executable(${YOUR_TARGET} ${_src}) # Tell qasc to preprocess your headers qas_wrap_cpp(_qasc_src ${_headers_for_qas} TARGET ${YOUR_TARGET}) # Add auto generated sources to target target_sources(${YOUR_TARGET} PRIVATE ${_qasc_src})
-
qas_wrap_cpp
simply adds a series of command to generate the extra source files containing the implementations, and return the extra sources list to variable_qasc_src
, you need to add them to the target. -
The codes in generated source files need a number of functions defined in
qjsonstream.h
.
Details
CMake Identifiers Intro
qas_wrap_cpp
qas_wrap_cpp(<VAR> src_file1 [src_file2 ...]
[TARGET target]
[OPTIONS ...]
[DEPENDS ...])
- This macro is modified from
qt5_wrap_cpp
in Qt cmake modules. - Creates rules for calling the Qt Auto Serialization Compiler (qasc) on the given source files. For each input file, an output file is generated in the build directory. The paths of the generated files are added to
<VAR>
. - You can set an explicit
TARGET
. This will make sure that the target propertiesINCLUDE_DIRECTORIES
andCOMPILE_DEFINITIONS
are also used when scanning the source files with qasc. - You can set additional
OPTIONS
that should be added to the qasc calls. You can find possible options in the qasc documentation. DEPENDS
allows you to add additional dependencies for recreation of the generated files. This is useful when the sources have implicit dependencies.
QASC Tool
-
qasc
is a lexical analyzer modified frommoc
in Qt5 tools, it preprocesses source files before building. If any one of the following identifiers appears in a given source file,qasc
will generate implementations of serializers and deserializers intostdout
or a new file.QAS_JSON
QAS_JSON_NS
-
qasc
has been tested when in Qt6 framework, it works fine.
Acknowledgements
- moc
- Qt Meta Object Compiler (Qt 5.15.2)
License
-
The moc compiler is a part of Qt sources which is released under GNU LGPL v2.1 or later, which
qasc
inherited. If you modify any codes inqasc
's source files, you need to make your modification open source as the LGPL requires. (Important!!!) -
Other codes in this project are released under Apache 2.0 License.