/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "json_input.h"
#include
#include
#include
#include
#include
#include
#include "../ser_nodes.h"
META_BEGIN_NAMESPACE()
namespace Serialization {
constexpr Version CURRENT_JSON_VERSION(2, 0);
constexpr int UID_SIZE = 36;
ISerNode::Ptr JsonInput::ImportRef(const json::value& ref)
{
if (ref.is_string()) {
RefUri uri;
// check for backward compatibility
if (ref.string_.substr(0, 4) == "ref:") {
uri = RefUri(CORE_NS::json::unescape(ref.string_));
} else {
uri.SetBaseObjectUid(BASE_NS::StringToUid(CORE_NS::json::unescape(ref.string_)));
}
if (uri.IsValid()) {
return ISerNode::Ptr(new RefNode(BASE_NS::move(uri)));
}
}
return nullptr;
}
BASE_NS::string ReadString(BASE_NS::string_view name, const json::value& value)
{
BASE_NS::string str;
if (const auto& v = value.find(name)) {
if (v->is_string()) {
str = CORE_NS::json::unescape(v->string_);
}
}
return str;
}
BASE_NS::Uid ReadUid(BASE_NS::string_view name, const json::value& value)
{
BASE_NS::Uid uid;
if (const auto& id = value.find(name)) {
if (id->is_string() && id->string_.size() == UID_SIZE) {
uid = BASE_NS::StringToUid(id->string_);
}
}
return uid;
}
static bool IsValidName(const BASE_NS::string_view& name)
{
return !name.empty() && name[0] != '$';
}
ISerNode::Ptr JsonInput::ImportObject(const json::value& value)
{
BASE_NS::string className = ReadString(ClassNameName, value);
BASE_NS::string name = ReadString(NameName, value);
ObjectId oid = ReadUid(ObjectIdName, value);
InstanceId iid = ReadUid(InstanceIdName, value);
BASE_NS::vector members;
for (auto&& p : value.object_) {
auto key = RewriteReservedName(p.key);
if (!IsValidName(key)) {
// Skip empty keys and keys starting with $
continue;
}
auto n = Import(p.value);
if (!n) {
return nullptr;
}
members.push_back(NamedNode { key, BASE_NS::move(n) });
}
auto map = CreateShared(BASE_NS::move(members));
if (oid.IsValid()) {
return ISerNode::Ptr(
new ObjectNode(BASE_NS::move(className), BASE_NS::move(name), oid, iid, BASE_NS::move(map)));
}
return map;
}
ISerNode::Ptr JsonInput::ImportArray(const json::value::array& arr)
{
BASE_NS::vector nodes;
nodes.reserve(arr.size());
for (auto&& value : arr) {
if (auto n = Import(value)) {
nodes.emplace_back(BASE_NS::move(n));
} else {
return nullptr;
}
}
return ISerNode::Ptr(new ArrayNode(BASE_NS::move(nodes)));
}
ISerNode::Ptr JsonInput::Import(const json::value& value)
{
switch (value.type) {
case json::type::boolean:
return ISerNode::Ptr(new BoolNode(value.boolean_));
case json::type::floating_point:
return ISerNode::Ptr(new DoubleNode(value.float_));
case json::type::signed_int:
return ISerNode::Ptr(new IntNode(value.signed_));
case json::type::unsigned_int:
return ISerNode::Ptr(new UIntNode(value.unsigned_));
case json::type::string:
return ISerNode::Ptr(new StringNode(CORE_NS::json::unescape(value.string_)));
case json::type::object:
if (auto ref = value.find("$ref")) {
return ImportRef(*ref);
}
return ImportObject(value);
case json::type::array:
return ImportArray(value.array_);
case json::type::null:
return ISerNode::Ptr(new NilNode);
default:
CORE_ASSERT_MSG(false, "Unhandled primitive type in Json input");
return nullptr;
}
}
bool JsonInput::ReadMetadata(const json::value& value)
{
if (auto v = value.find("version")) {
if (v->is_string()) {
auto ver = Version(CORE_NS::json::unescape(v->string_));
if (ver == Version()) {
CORE_LOG_E(
"Invalid file version: %s != %s", ver.ToString().c_str(), CURRENT_JSON_VERSION.ToString().c_str());
return false;
}
jsonVersion_ = ver;
}
}
if (auto v = value.find("exporter-version")) {
if (v->is_string()) {
exporterVersion_ = Version(CORE_NS::json::unescape(v->string_));
}
}
return true;
}
ISerNode::Ptr JsonInput::ImportRootObject(const json::value& value)
{
// see if we have meta data
if (auto v = value.find("$meta")) {
if (v->is_object() && !ReadMetadata(*v)) {
return nullptr;
}
}
json::value root;
// is it legacy version?
if (jsonVersion_ == Version {}) {
jsonVersion_ = Version { 1, 0 };
root = value;
} else if (auto v = value.find("$root")) {
if (v->is_object()) {
root = *v;
}
}
if (jsonVersion_ < Version(2, 0)) {
SetMetaV1Compatibility();
}
auto obj = Import(root);
return obj ? ISerNode::Ptr(new RootNode(exporterVersion_, obj)) : nullptr;
}
ISerNode::Ptr JsonInput::Process(BASE_NS::string_view data)
{
ISerNode::Ptr res;
auto json = CORE_NS::json::parse(data.data());
if (json.is_object()) {
res = ImportRootObject(json);
}
return res;
}
void JsonInput::SetMetaV1Compatibility()
{
CORE_LOG_I("Enabling Meta Object Version 1 compatibility");
ClassNameName = "$name";
NameName = "";
RewriteReservedName = [](auto name) {
if (name == "$properties") {
return BASE_NS::string("__properties");
}
if (name == "$flags") {
return BASE_NS::string("__flags");
}
return BASE_NS::string(name);
};
}
} // namespace Serialization
META_END_NAMESPACE()