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