Skip to content

Commit

Permalink
feat: add json features
Browse files Browse the repository at this point in the history
  • Loading branch information
ZgblKylin committed Apr 19, 2021
1 parent fb3354d commit 901a378
Show file tree
Hide file tree
Showing 23 changed files with 499 additions and 261 deletions.
32 changes: 21 additions & 11 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ project(KtUtils LANGUAGES C CXX)
SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib)
set(CMAKE_DEBUG_POSTFIX d)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD 14)

# Setup detailed warning
if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
Expand All @@ -25,6 +25,7 @@ endif()
# Setup static build option
option(BUILD_SHARED_LIBS "Build/link shared library" OFF)
option(BUILD_TESTING "Build test" OFF)
option(BUILD_EXAMPLES "Build examples" OFF)
if(NOT BUILD_SHARED_LIBS)
# Use /MT /MTd instead of /MD /MDd when building static library
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
Expand Down Expand Up @@ -62,23 +63,27 @@ endif()

target_precompile_headers(${PROJECT_NAME}
INTERFACE
${CMAKE_CURRENT_LIST_DIR}/include/KtUtils/ktutils.hpp
${CMAKE_CURRENT_LIST_DIR}/include/KtUtils/KtUtils.hpp
PRIVATE
${CMAKE_CURRENT_LIST_DIR}/include/KtUtils/global.hpp
${CMAKE_CURRENT_LIST_DIR}/include/KtUtils/Global.hpp
)

target_sources(${PROJECT_NAME}
PRIVATE
${CMAKE_CURRENT_LIST_DIR}/include/KtUtils/ktutils.hpp
${CMAKE_CURRENT_LIST_DIR}/include/KtUtils/KtUtils.hpp

${CMAKE_CURRENT_LIST_DIR}/include/KtUtils/global.hpp
${CMAKE_CURRENT_LIST_DIR}/src/global.cpp
${CMAKE_CURRENT_LIST_DIR}/include/KtUtils/Global.hpp
${CMAKE_CURRENT_LIST_DIR}/src/Global.cpp

${CMAKE_CURRENT_LIST_DIR}/include/KtUtils/iconhelper.hpp
${CMAKE_CURRENT_LIST_DIR}/src/iconhelper.cpp
${CMAKE_CURRENT_LIST_DIR}/include/KtUtils/IconHelper.hpp
${CMAKE_CURRENT_LIST_DIR}/src/IconHelper.cpp

${CMAKE_CURRENT_LIST_DIR}/include/KtUtils/settings.hpp
${CMAKE_CURRENT_LIST_DIR}/src/settings.cpp
${CMAKE_CURRENT_LIST_DIR}/include/KtUtils/Json.hpp
${CMAKE_CURRENT_LIST_DIR}/src/Json.cpp

${CMAKE_CURRENT_LIST_DIR}/include/KtUtils/Settings.hpp
${CMAKE_CURRENT_LIST_DIR}/src/Settings_p.hpp
${CMAKE_CURRENT_LIST_DIR}/src/Settings.cpp

${CMAKE_CURRENT_LIST_DIR}/KtUtils.qrc
)
Expand Down Expand Up @@ -108,8 +113,13 @@ target_include_directories(${PROJECT_NAME}


# Setup test
if (BUILD_TESTING)
if(BUILD_TESTING)
enable_testing()
add_subdirectory(test)
add_test(NAME TestGlobal COMMAND TestGlobal)
endif()

# Build example
if(BUILD_EXAMPLES)
add_subdirectory(examples)
endif()
31 changes: 31 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Setup CMake
cmake_minimum_required(VERSION 3.15)
project(KtUtils_Example LANGUAGES C CXX)



# Setup Qt
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
# set(CMAKE_INCLUDE_CURRENT_DIR ON) # if(CMAKE_VERSION VERSION_LESS "3.7.0")
find_package(Qt5
COMPONENTS
Core
Gui
Widgets
REQUIRED
)



# Json
add_executable(${PROJECT_NAME}_Json WIN32)
target_sources(${PROJECT_NAME}_Json PRIVATE ${CMAKE_CURRENT_LIST_DIR}/Json.cpp)
target_link_libraries(${PROJECT_NAME}_Json
PUBLIC
Qt5::Core
Qt5::Gui
Qt5::Widgets
KtUtils
)
48 changes: 48 additions & 0 deletions examples/Json.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#include <KtUtils/Json>

int main(int argc, char* argv[]) {
QApplication app(argc, argv);

QString path = QFileDialog::getOpenFileName(
nullptr, QStringLiteral("Open json file"), QString(),
QStringLiteral("json file(*.json)"));
QFileInfo fileInfo(path);
if (!fileInfo.exists()) return EXIT_FAILURE;

QFile file(fileInfo.absoluteFilePath());
if (!file.open(QFile::ReadOnly | QFile::Text)) return EXIT_FAILURE;

QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &error);
if (error.error != QJsonParseError::NoError) return EXIT_FAILURE;
file.close();

QTreeView tree;
QStandardItemModel* model = new QStandardItemModel;
tree.setModel(model);
QList<QStandardItem*> row = KtUtils::Json::toStandardItem(
doc.isArray() ? QJsonValue(doc.array()) : QJsonValue(doc.object()),
fileInfo.fileName());
model->appendRow(row);
// Object only return 1 item, force model to display 2 columns
model->setColumnCount(2);
tree.setRootIndex(model->invisibleRootItem()->index());

tree.setWindowTitle(fileInfo.absoluteFilePath());
tree.header()->setSectionResizeMode(QHeaderView::ResizeToContents);
tree.header()->setResizeContentsPrecision(0);
tree.setEditTriggers(QAbstractItemView::NoEditTriggers);
tree.expandAll();
tree.show();

QJsonValue json = KtUtils::Json::fromStandardItem(row.at(0));
doc = QJsonDocument(json.toObject());
QFile output(fileInfo.absoluteDir().absoluteFilePath(
fileInfo.completeBaseName() + "_out.json"));
output.open(QFile::WriteOnly | QFile::Text | QFile::Truncate);
output.write(doc.toJson(QJsonDocument::Indented));
output.close();
QDesktopServices::openUrl(output.fileName());

return app.exec();
}
2 changes: 1 addition & 1 deletion include/KtUtils/Global
Original file line number Diff line number Diff line change
@@ -1 +1 @@
#include "global.hpp"
#include "Global.hpp"
File renamed without changes.
2 changes: 1 addition & 1 deletion include/KtUtils/IconHelper
Original file line number Diff line number Diff line change
@@ -1 +1 @@
#include "iconhelper.hpp"
#include "IconHelper.hpp"
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#ifndef KTUTILS_ICONHELPER_HPP
#define KTUTILS_ICONHELPER_HPP

#include "global.hpp"
#include "Global.hpp"

namespace KtUtils {
/**
Expand Down
1 change: 1 addition & 0 deletions include/KtUtils/Json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#include "Json.hpp"
37 changes: 37 additions & 0 deletions include/KtUtils/Json.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#pragma once
#ifndef KT_UTILS_JSON_HPP
#define KT_UTILS_JSON_HPP

#include "Global.hpp"

namespace KtUtils {
namespace Json {
/** \brief Generate QStandardItem tree from give json.
* \param json json to generate the tree from.
* \param name name for the return item.
* \return List of items for a tree row.
* First: Item with text `name`, contains children(if has).
* Second(if exist): value when json is neither object nor array. */
QList<QStandardItem*> KTUTILS_EXPORT toStandardItem(const QJsonValue& json,
const QString& name = {});

/** \brief Generate json value from QStandardItem tree.
* \param col0 Item at column 0, contains item name and children.
* \param col1 Item at column 1, contains item value.
* \return
* QJsonObject if roow is a tree.
* QJsonArray if all direct children are named 0,1,...,n.
* QJsonValue for elsewise. */
QJsonValue KTUTILS_EXPORT fromStandardItem(QStandardItem* col0,
QStandardItem* col1 = nullptr);

// QSettings::registerFormat
KTUTILS_EXPORT bool settingsReadFunc(QIODevice& device,
QSettings::SettingsMap& map);
// QSettings::registerFormat
KTUTILS_EXPORT bool settingsWriteFunc(QIODevice& device,
const QSettings::SettingsMap& map);
} // namespace Json
} // namespace KtUtils

#endif // KT_UTILS_JSON_HPP
2 changes: 1 addition & 1 deletion include/KtUtils/KtUtils
Original file line number Diff line number Diff line change
@@ -1 +1 @@
#include "ktutils.hpp"
#include "KtUtils.hpp"
7 changes: 4 additions & 3 deletions include/KtUtils/ktutils.hpp → include/KtUtils/KtUtils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
#ifndef KTUTILS_KTUTILS_HPP
#define KTUTILS_KTUTILS_HPP

#include "global.hpp"
#include "iconhelper.hpp"
#include "settings.hpp"
#include "Global.hpp"
#include "IconHelper.hpp"
#include "Json.hpp"
#include "Settings.hpp"

#endif // KTUTILS_KTUTILS_HPP
2 changes: 1 addition & 1 deletion include/KtUtils/Settings
Original file line number Diff line number Diff line change
@@ -1 +1 @@
#include "settings.hpp"
#include "Settings.hpp"
25 changes: 25 additions & 0 deletions include/KtUtils/Settings.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#pragma once
#ifndef KTUTILS_SETTINGS_HPP
#define KTUTILS_SETTINGS_HPP

#include "Global.hpp"
#include "Json.hpp"

namespace KtUtils {
// Helper functions for QSettings::registerFormat
namespace SettingsExtra {
inline bool jsonReadFunc(QIODevice& device, QSettings::SettingsMap& map) {
return ::KtUtils::Json::settingsReadFunc(device, map);
}
inline bool jsonWriteFunc(QIODevice& device,
const QSettings::SettingsMap& map) {
return ::KtUtils::Json::settingsWriteFunc(device, map);
}

KTUTILS_EXPORT bool xmlReadFunc(QIODevice& device, QSettings::SettingsMap& map);
KTUTILS_EXPORT bool xmlWriteFunc(QIODevice& device,
const QSettings::SettingsMap& map);
} // namespace SettingsExtra
} // namespace KtUtils

#endif // KTUTILS_SETTINGS_HPP
22 changes: 0 additions & 22 deletions include/KtUtils/settings.hpp

This file was deleted.

File renamed without changes.
File renamed without changes.
113 changes: 113 additions & 0 deletions src/Json.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#include "KtUtils/Json.hpp"
#include "Settings_p.hpp"

namespace KtUtils {
namespace Json {
QList<QStandardItem*> toStandardItem(QVariant name, const QJsonValue& json) {
QList<QList<QStandardItem*>> children;

QStandardItem* col0 = new QStandardItem;
col0->setData(name, Qt::EditRole);
col0->setData(name, Qt::DisplayRole);
col0->setData(name, Qt::AccessibleTextRole);

switch (json.type()) {
case QJsonValue::Null:
return {col0};

case QJsonValue::Object: {
QJsonObject obj = json.toObject();
for (auto it = obj.constBegin(); it != obj.constEnd(); ++it) {
children << toStandardItem(it.key(), it.value());
}
break;
}

case QJsonValue::Array: {
QJsonArray array = json.toArray();
for (int i = 0; i < array.count(); ++i) {
children << toStandardItem(i, array.at(i));
}
break;
}

default: {
QStandardItem* col1 = new QStandardItem();
col1->setData(json, Qt::EditRole);
col1->setData(json.toVariant(), Qt::DisplayRole);
col1->setData(json.toVariant(), Qt::AccessibleTextRole);
return {col0, col1};
}
}

// Object or array.
for (auto&& child : children) {
col0->appendRow(child);
}
return {col0};
}

QList<QStandardItem*> toStandardItem(const QJsonValue& json,
const QString& name) {
return toStandardItem(name, json);
}

QJsonValue fromStandardItem(QStandardItem* col0, QStandardItem* col1) {
if (!col0) return QJsonValue();
if (!col0->hasChildren()) {
if (col1) {
return QJsonValue::fromVariant(col1->data(Qt::EditRole));
} else {
return QJsonValue();
}
}

QVariant value = col0->child(0, 0)->data(Qt::EditRole);
bool isArray = value.canConvert<int>();
if (isArray) {
int begin = value.toInt();
isArray = (value == 0) || (value == 1);
for (int row = 0; isArray && (row < col0->rowCount()); ++row) {
value = col0->child(0, 0)->data(Qt::EditRole);
isArray = value.canConvert<int>() && (value.toInt() == (begin + row));
}
}

if (isArray) {
QJsonArray array;
for (int row = 0; row < col0->rowCount(); ++row) {
array << fromStandardItem(col0->child(row, 0), col0->child(row, 1));
}
return array;
} else {
QJsonObject object;
for (int rpw = 0; rpw < col0->rowCount(); ++rpw) {
QStandardItem* first = col0->child(rpw, 0);
QStandardItem* second = col0->child(rpw, 1);
object[first->data(Qt::DisplayRole).toString()] =
fromStandardItem(first, second);
}
return object;
}
}

bool settingsReadFunc(QIODevice& device, QSettings::SettingsMap& map) {
QJsonParseError error;
const QJsonDocument doc = QJsonDocument::fromJson(device.readAll(), &error);
if (error.error != QJsonParseError::NoError) {
qWarning("Json parse error at pos %d: %s", error.offset,
error.errorString().toUtf8().constData());
return false;
}
QVariantMap variantMap = doc.object().toVariantMap();
map = ::KtUtils::SettingsExtra::ToSettingsMap(variantMap);
return true;
}

bool settingsWriteFunc(QIODevice& device, const QSettings::SettingsMap& map) {
QVariantMap variantMap = ::KtUtils::SettingsExtra::FromSettingsMap(map);
const QJsonDocument doc(QJsonObject::fromVariantMap(variantMap));
return device.write(doc.toJson(QJsonDocument::Indented)) >= 0;
}
} // namespace Json
} // namespace KtUtils
Loading

0 comments on commit 901a378

Please sign in to comment.