1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16
17 #include "json_input.h"
18
19 #include <base/containers/vector.h>
20 #include <base/util/uid_util.h>
21
22 #include <meta/base/namespace.h>
23 #include <meta/base/plugin.h>
24 #include <meta/base/ref_uri.h>
25 #include <meta/ext/minimal_object.h>
26
27 #include "../ser_nodes.h"
28
29 META_BEGIN_NAMESPACE()
30
31 namespace Serialization {
32
33 constexpr Version CURRENT_JSON_VERSION(2, 0);
34 constexpr int UID_SIZE = 36;
35
ImportRef(const json::value & ref)36 ISerNode::Ptr JsonInput::ImportRef(const json::value& ref)
37 {
38 if (ref.is_string()) {
39 RefUri uri;
40 // check for backward compatibility
41 if (ref.string_.substr(0, 4) == "ref:") {
42 uri = RefUri(CORE_NS::json::unescape(ref.string_));
43 } else {
44 uri.SetBaseObjectUid(BASE_NS::StringToUid(CORE_NS::json::unescape(ref.string_)));
45 }
46 if (uri.IsValid()) {
47 return ISerNode::Ptr(new RefNode(BASE_NS::move(uri)));
48 }
49 }
50 return nullptr;
51 }
52
ReadString(BASE_NS::string_view name,const json::value & value)53 BASE_NS::string ReadString(BASE_NS::string_view name, const json::value& value)
54 {
55 BASE_NS::string str;
56 if (const auto& v = value.find(name)) {
57 if (v->is_string()) {
58 str = CORE_NS::json::unescape(v->string_);
59 }
60 }
61 return str;
62 }
63
ReadUid(BASE_NS::string_view name,const json::value & value)64 BASE_NS::Uid ReadUid(BASE_NS::string_view name, const json::value& value)
65 {
66 BASE_NS::Uid uid;
67 if (const auto& id = value.find(name)) {
68 if (id->is_string() && id->string_.size() == UID_SIZE) {
69 uid = BASE_NS::StringToUid(id->string_);
70 }
71 }
72 return uid;
73 }
74
IsValidName(const BASE_NS::string_view & name)75 static bool IsValidName(const BASE_NS::string_view& name)
76 {
77 return !name.empty() && name[0] != '$';
78 }
79
ImportObject(const json::value & value)80 ISerNode::Ptr JsonInput::ImportObject(const json::value& value)
81 {
82 BASE_NS::string className = ReadString(ClassNameName, value);
83 BASE_NS::string name = ReadString(NameName, value);
84 ObjectId oid = ReadUid(ObjectIdName, value);
85 InstanceId iid = ReadUid(InstanceIdName, value);
86
87 BASE_NS::vector<NamedNode> members;
88 for (auto&& p : value.object_) {
89 auto key = RewriteReservedName(p.key);
90 if (!IsValidName(key)) {
91 // Skip empty keys and keys starting with $
92 continue;
93 }
94 auto n = Import(p.value);
95 if (!n) {
96 return nullptr;
97 }
98 members.push_back(NamedNode { key, BASE_NS::move(n) });
99 }
100 auto map = CreateShared<MapNode>(BASE_NS::move(members));
101 if (oid.IsValid()) {
102 return ISerNode::Ptr(
103 new ObjectNode(BASE_NS::move(className), BASE_NS::move(name), oid, iid, BASE_NS::move(map)));
104 }
105 return map;
106 }
107
ImportArray(const json::value::array & arr)108 ISerNode::Ptr JsonInput::ImportArray(const json::value::array& arr)
109 {
110 BASE_NS::vector<ISerNode::Ptr> nodes;
111 nodes.reserve(arr.size());
112 for (auto&& value : arr) {
113 if (auto n = Import(value)) {
114 nodes.emplace_back(BASE_NS::move(n));
115 } else {
116 return nullptr;
117 }
118 }
119 return ISerNode::Ptr(new ArrayNode(BASE_NS::move(nodes)));
120 }
121
Import(const json::value & value)122 ISerNode::Ptr JsonInput::Import(const json::value& value)
123 {
124 switch (value.type) {
125 case json::type::boolean:
126 return ISerNode::Ptr(new BoolNode(value.boolean_));
127 case json::type::floating_point:
128 return ISerNode::Ptr(new DoubleNode(value.float_));
129 case json::type::signed_int:
130 return ISerNode::Ptr(new IntNode(value.signed_));
131 case json::type::unsigned_int:
132 return ISerNode::Ptr(new UIntNode(value.unsigned_));
133 case json::type::string:
134 return ISerNode::Ptr(new StringNode(CORE_NS::json::unescape(value.string_)));
135 case json::type::object:
136 if (auto ref = value.find("$ref")) {
137 return ImportRef(*ref);
138 }
139 return ImportObject(value);
140 case json::type::array:
141 return ImportArray(value.array_);
142 case json::type::null:
143 return ISerNode::Ptr(new NilNode);
144 default:
145 CORE_ASSERT_MSG(false, "Unhandled primitive type in Json input");
146 return nullptr;
147 }
148 }
149
ReadMetadata(const json::value & value)150 bool JsonInput::ReadMetadata(const json::value& value)
151 {
152 if (auto v = value.find("version")) {
153 if (v->is_string()) {
154 auto ver = Version(CORE_NS::json::unescape(v->string_));
155 if (ver == Version()) {
156 CORE_LOG_E(
157 "Invalid file version: %s != %s", ver.ToString().c_str(), CURRENT_JSON_VERSION.ToString().c_str());
158 return false;
159 }
160 version_ = ver;
161 }
162 }
163 if (auto v = value.find("exporter-version")) {
164 if (v->is_string()) {
165 exporterVersion_ = Version(CORE_NS::json::unescape(v->string_));
166 }
167 }
168 return true;
169 }
170
ImportRootObject(const json::value & value)171 ISerNode::Ptr JsonInput::ImportRootObject(const json::value& value)
172 {
173 // see if we have meta data
174 if (auto v = value.find("$meta")) {
175 if (v->is_object() && !ReadMetadata(*v)) {
176 return nullptr;
177 }
178 }
179
180 json::value root;
181
182 // is it legacy version?
183 if (version_ == Version {}) {
184 version_ = Version { 1, 0 };
185 root = value;
186 } else if (auto v = value.find("$root")) {
187 if (v->is_object()) {
188 root = *v;
189 }
190 }
191 if (version_ < Version(2, 0)) {
192 SetMetaV1Compatibility();
193 }
194
195 auto obj = Import(root);
196 return obj ? ISerNode::Ptr(new RootNode(obj, version_, exporterVersion_)) : nullptr;
197 }
198
Process(BASE_NS::string_view data)199 ISerNode::Ptr JsonInput::Process(BASE_NS::string_view data)
200 {
201 ISerNode::Ptr res;
202 auto json = CORE_NS::json::parse(data.data());
203 if (json.is_object()) {
204 res = ImportRootObject(json);
205 }
206 return res;
207 }
208
SetMetaV1Compatibility()209 void JsonInput::SetMetaV1Compatibility()
210 {
211 CORE_LOG_I("Enabling Meta Object Version 1 compatibility");
212 ClassNameName = "$name";
213 NameName = "";
214 RewriteReservedName = [](auto name) {
215 if (name == "$properties") {
216 return BASE_NS::string("__properties");
217 }
218 if (name == "$flags") {
219 return BASE_NS::string("__flags");
220 }
221 return BASE_NS::string(name);
222 };
223 }
224
225 } // namespace Serialization
226
227 META_END_NAMESPACE()
228