Revision control

Copy as Markdown

// Copyright (C) 2026 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qohosjsonconversions_p.h"
#include <QtCore/qjsonarray.h>
#include <QtCore/qjsonvalue.h>
#include <QtCore/qstring.h>
#include <cstdint>
#include <napi.h>
QT_BEGIN_NAMESPACE
namespace QtOhos {
namespace {
constexpr int napiToJsonMappingNestingLimit = 1024;
QJsonValue mapNapiValueToJsonValue(const QNapi::Value &napiValue, int remainingNestingLevels);
QJsonObject mapNapiObjectToJsonObjectImpl(const QNapi::Object &napiObject, int remainingNestingLevels);
QNapi::Value mapJsonValueToNapiValue(napi_env env, const QJsonValue &jsonValue);
void checkNapiToJsonMappingRemainingNestingLevels(napi_env env, int remainingNestingLevels)
{
if (remainingNestingLevels == 0)
throw QNapi::makeLoggedException(env, "JS value nested too deeply to be mapped to JSON");
}
QJsonArray mapNapiArrayToJsonArray(const QNapi::Array &napiArray, int remainingNestingLevels)
{
checkNapiToJsonMappingRemainingNestingLevels(napiArray.Env(), remainingNestingLevels);
QJsonArray jsonArray;
const std::uint32_t arrayLength = napiArray.Length();
for (std::uint32_t i = 0; i < arrayLength; ++i) {
Napi::HandleScope elementScope(napiArray.Env());
auto jsonValue = mapNapiValueToJsonValue(napiArray.Get(i), remainingNestingLevels);
jsonArray.append(jsonValue.isUndefined() ? QJsonValue(QJsonValue::Null) : jsonValue);
}
return jsonArray;
}
QNapi::Array mapJsonArrayToNapiArray(napi_env env, const QJsonArray &jsonArray)
{
return QNapi::runEscapingHandleScope<QNapi::Array>(
env,
[&]() {
const auto arrayLength = static_cast<std::uint32_t>(jsonArray.size());
auto napiArray = QNapi::Array::New(env, arrayLength);
for (std::uint32_t i = 0; i < arrayLength; ++i)
napiArray.Set(i, mapJsonValueToNapiValue(env, jsonArray.at(i)));
return napiArray;
});
}
QJsonValue mapNapiValueToJsonValue(const QNapi::Value &napiValue, int remainingNestingLevels)
{
if (napiValue.IsNull())
return QJsonValue(QJsonValue::Null);
if (napiValue.IsBoolean())
return QJsonValue(QNapi::checkedCast<QNapi::Boolean>(napiValue).Value());
if (napiValue.IsNumber())
return QJsonValue(QNapi::checkedCast<QNapi::Number>(napiValue).DoubleValue());
if (napiValue.IsString())
return QJsonValue(QString::fromStdString(QNapi::checkedCast<QNapi::String>(napiValue)));
if (napiValue.IsArray())
return mapNapiArrayToJsonArray(QNapi::checkedCast<QNapi::Array>(napiValue), remainingNestingLevels - 1);
if (napiValue.IsObject() && !napiValue.IsFunction())
return mapNapiObjectToJsonObjectImpl(QNapi::checkedCast<QNapi::Object>(napiValue), remainingNestingLevels - 1);
return QJsonValue(QJsonValue::Undefined);
}
QNapi::Value mapJsonValueToNapiValue(napi_env env, const QJsonValue &jsonValue)
{
switch (jsonValue.type()) {
case QJsonValue::Null:
case QJsonValue::Undefined:
return Napi::Env(env).Null();
case QJsonValue::Bool:
return QNapi::Boolean::New(env, jsonValue.toBool());
case QJsonValue::Double:
return QNapi::Number::New(env, jsonValue.toDouble());
case QJsonValue::String:
return QNapi::String::New(env, jsonValue.toString().toStdString());
case QJsonValue::Array:
return mapJsonArrayToNapiArray(env, jsonValue.toArray());
case QJsonValue::Object:
return mapJsonObjectToNapiObject(env, jsonValue.toObject());
}
Q_UNREACHABLE_RETURN(QNapi::Value());
}
QJsonObject mapNapiObjectToJsonObjectImpl(const QNapi::Object &napiObject, int remainingNestingLevels)
{
checkNapiToJsonMappingRemainingNestingLevels(napiObject.Env(), remainingNestingLevels);
QJsonObject jsonObject;
for (const auto &propertyName : napiObject.getFilteredStringKeys(napi_key_own_only, napi_key_enumerable)) {
auto jsonValue = mapNapiValueToJsonValue(napiObject.Get(propertyName), remainingNestingLevels);
if (!jsonValue.isUndefined())
jsonObject.insert(QString::fromStdString(propertyName), jsonValue);
}
return jsonObject;
}
}
QJsonObject mapNapiObjectToJsonObject(const QNapi::Object &napiObject)
{
return mapNapiObjectToJsonObjectImpl(napiObject, napiToJsonMappingNestingLimit);
}
QNapi::Object mapJsonObjectToNapiObject(napi_env env, const QJsonObject &jsonObject)
{
return QNapi::runEscapingHandleScope<QNapi::Object>(
env,
[&]() {
auto napiObject = QNapi::Object::New(env);
for (auto propertyIter = jsonObject.begin(); propertyIter != jsonObject.end(); ++propertyIter)
napiObject.Set(propertyIter.key().toStdString(), mapJsonValueToNapiValue(env, propertyIter.value()));
return napiObject;
});
}
}
QT_END_NAMESPACE