1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 // names, trademarks, service marks, or product names of the Licensor
11 // and its affiliates, except as required to comply with Section 4(c) of
12 // the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 // http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 ///
25 /// \file js/json.cpp
26
27 #include "pxr/pxr.h"
28 #include "pxr/base/js/json.h"
29
30 #include "pxr/base/tf/diagnostic.h"
31 #include "pxr/base/tf/stringUtils.h"
32
33 #include <iostream>
34 #include <vector>
35
36 #if PXR_USE_NAMESPACES
37 #define PXRJS PXR_NS
38 #else
39 #define PXRJS PXRJS
40 #endif
41
42 // Place rapidjson into a namespace to prevent conflicts with d2.
43 #define RAPIDJSON_NAMESPACE PXRJS::rapidjson
44 #define RAPIDJSON_NAMESPACE_BEGIN namespace PXRJS { namespace rapidjson {
45 #define RAPIDJSON_NAMESPACE_END } }
46
47 #include "rapidjson/allocators.h"
48 #include "rapidjson/document.h"
49 #include "rapidjson/reader.h"
50 #include "rapidjson/ostreamwrapper.h"
51 #include "rapidjson/prettywriter.h"
52 #include "rapidjson/error/error.h"
53 #include "rapidjson/error/en.h"
54
55 namespace rj = RAPIDJSON_NAMESPACE;
56
57 namespace {
58 PXR_NAMESPACE_USING_DIRECTIVE
59
60 struct _InputHandler : public rj::BaseReaderHandler<rj::UTF8<>, _InputHandler>
61 {
Null__anon2a1bb4850111::_InputHandler62 bool Null() {
63 values.emplace_back();
64 return true;
65 }
Bool__anon2a1bb4850111::_InputHandler66 bool Bool(bool b) {
67 values.emplace_back(b);
68 return true;
69 }
Int__anon2a1bb4850111::_InputHandler70 bool Int(int i) {
71 values.emplace_back(i);
72 return true;
73 }
Uint__anon2a1bb4850111::_InputHandler74 bool Uint(unsigned u) {
75 values.emplace_back(static_cast<uint64_t>(u));
76 return true;
77 }
Int64__anon2a1bb4850111::_InputHandler78 bool Int64(int64_t i) {
79 values.emplace_back(i);
80 return true;
81 }
Uint64__anon2a1bb4850111::_InputHandler82 bool Uint64(uint64_t u) {
83 values.emplace_back(u);
84 return true;
85 }
Double__anon2a1bb4850111::_InputHandler86 bool Double(double d) {
87 values.emplace_back(d);
88 return true;
89 }
String__anon2a1bb4850111::_InputHandler90 bool String(const char* str, rj::SizeType length, bool /* copy */) {
91 values.emplace_back(std::string(str, length));
92 return true;
93 }
Key__anon2a1bb4850111::_InputHandler94 bool Key(const char* str, rj::SizeType length, bool /* copy */) {
95 keys.emplace_back(str, length);
96 return true;
97 }
StartObject__anon2a1bb4850111::_InputHandler98 bool StartObject() {
99 return true;
100 }
EndObject__anon2a1bb4850111::_InputHandler101 bool EndObject(rj::SizeType memberCount) {
102 const size_t kstart = keys.size() - memberCount;
103 const size_t vstart = values.size() - memberCount;
104
105 JsObject object;
106 for (size_t i = 0; i < memberCount; ++i) {
107 object.insert(
108 std::make_pair(
109 std::move(keys[kstart + i]),
110 std::move(values[vstart + i])));
111 }
112
113 keys.resize(kstart);
114 values.resize(vstart);
115
116 values.emplace_back(std::move(object));
117 return true;
118 }
BeginArray__anon2a1bb4850111::_InputHandler119 bool BeginArray() {
120 return true;
121 }
EndArray__anon2a1bb4850111::_InputHandler122 bool EndArray(rj::SizeType elementCount) {
123 std::vector<JsValue> valueArray(
124 values.end() - elementCount, values.end());
125 values.resize(values.size() - elementCount);
126 values.emplace_back(std::move(valueArray));
127 return true;
128 }
129
130 public:
131 std::vector<JsObject::key_type> keys;
132 std::vector<JsObject::mapped_type> values;
133 };
134
135 // This class is needed to override writing out doubles. There is a bug in
136 // rapidJSON when writing out some double values. These classes uses the Tf
137 // library to do the conversion instead.
138 // See: https://github.com/Tencent/rapidjson/issues/954
139
140 template <class TBase>
141 class _WriterFix : public TBase
142 {
143 public:
144 using Base = TBase;
145 using Base::Base;
146
Double(double d)147 bool Double(double d) {
148 constexpr int bufferSize = 32;
149 char buffer[bufferSize];
150 TfDoubleToString(d, buffer, bufferSize, true);
151
152 return Base::RawValue(buffer, strlen(buffer), rj::kNumberType);
153 }
154 };
155
156 }
157
158 PXR_NAMESPACE_OPEN_SCOPE
159
160 template <typename Allocator>
161 static rj::Value
_ToImplObjectValue(const JsObject & object,Allocator & allocator)162 _ToImplObjectValue(
163 const JsObject& object,
164 Allocator& allocator)
165 {
166 rj::Value value(rj::kObjectType);
167
168 for (const auto& p : object) {
169 value.AddMember(
170 rj::Value(p.first.c_str(), allocator).Move(),
171 _JsValueToImplValue(p.second, allocator),
172 allocator);
173 }
174
175 return value;
176 }
177
178 template <typename Allocator>
179 static rj::Value
_ToImplArrayValue(const JsArray & array,Allocator & allocator)180 _ToImplArrayValue(
181 const JsArray& array,
182 Allocator& allocator)
183 {
184 rj::Value value(rj::kArrayType);
185
186 for (const auto& e : array) {
187 value.PushBack(
188 rj::Value(_JsValueToImplValue(e, allocator)).Move(),
189 allocator);
190 }
191
192 return value;
193 }
194
195 template <typename Allocator>
196 static rj::Value
_JsValueToImplValue(const JsValue & value,Allocator & allocator)197 _JsValueToImplValue(
198 const JsValue& value,
199 Allocator& allocator)
200 {
201 switch (value.GetType()) {
202 case JsValue::ObjectType:
203 return _ToImplObjectValue(value.GetJsObject(), allocator);
204 case JsValue::ArrayType:
205 return _ToImplArrayValue(value.GetJsArray(), allocator);
206 case JsValue::BoolType:
207 return rj::Value(value.GetBool());
208 case JsValue::StringType:
209 return rj::Value(value.GetString().c_str(), allocator);
210 case JsValue::RealType:
211 return rj::Value(value.GetReal());
212 case JsValue::IntType:
213 return value.IsUInt64() ?
214 rj::Value(value.GetUInt64()) : rj::Value(value.GetInt64());
215 case JsValue::NullType:
216 return rj::Value();
217 default: {
218 TF_CODING_ERROR("Unknown JsValue type");
219 return rj::Value();
220 }
221 }
222 }
223
224
225 JsValue
JsParseStream(std::istream & istr,JsParseError * error)226 JsParseStream(
227 std::istream& istr,
228 JsParseError* error)
229 {
230 if (!istr) {
231 TF_CODING_ERROR("Stream error");
232 return JsValue();
233 }
234
235 // Parse streams by reading into a string first. This makes it easier to
236 // yield good error messages that include line and column numbers, rather
237 // than the character offset that rapidjson currently provides.
238 return JsParseString(std::string(
239 (std::istreambuf_iterator<char>(istr)),
240 std::istreambuf_iterator<char>()),
241 error);
242 }
243
244 JsValue
JsParseString(const std::string & data,JsParseError * error)245 JsParseString(
246 const std::string& data,
247 JsParseError* error)
248 {
249 if (data.empty()) {
250 TF_CODING_ERROR("JSON string is empty");
251 return JsValue();
252 }
253
254 _InputHandler handler;
255 rj::Reader reader;
256 rj::StringStream ss(data.c_str());
257 // Need Full precision flag to round trip double values correctly.
258 rj::ParseResult result =
259 reader.Parse<rj::kParseFullPrecisionFlag|rj::kParseStopWhenDoneFlag>(
260 ss, handler);
261
262 if (!result) {
263 if (error) {
264 // Rapidjson only provides a character offset for errors, not
265 // line/column information like other parsers (like json_spirit,
266 // upon which this library was previously implemented). Analyze
267 // the input data to convert the offset to line/column.
268 error->line = 1;
269 const size_t eoff = result.Offset();
270 size_t nlpos = 0;
271 for (size_t i = 0; i < eoff; ++i) {
272 if (data[i] == '\n') {
273 error->line++;
274 nlpos = i;
275 }
276 }
277 error->column = eoff - nlpos;
278 error->reason = rj::GetParseError_En(result.Code());
279 }
280 return JsValue();
281 }
282
283 TF_VERIFY(handler.values.size() == 1,
284 "Unexpected value count: %zu", handler.values.size());
285
286 return handler.values.empty() ? JsValue() : handler.values.front();
287 }
288
289 void
JsWriteToStream(const JsValue & value,std::ostream & ostr)290 JsWriteToStream(
291 const JsValue& value,
292 std::ostream& ostr)
293 {
294 if (!ostr) {
295 TF_CODING_ERROR("Stream error");
296 return;
297 }
298
299 rj::Document d;
300 const rj::Value ivalue = _JsValueToImplValue(value, d.GetAllocator());
301
302 rj::OStreamWrapper os(ostr);
303 _WriterFix<rj::PrettyWriter<rj::OStreamWrapper>> writer(os);
304 writer.SetFormatOptions(rj::kFormatSingleLineArray);
305 ivalue.Accept(writer);
306 }
307
308 std::string
JsWriteToString(const JsValue & value)309 JsWriteToString(
310 const JsValue& value)
311 {
312 rj::Document d;
313 const rj::Value ivalue = _JsValueToImplValue(value, d.GetAllocator());
314
315 rj::StringBuffer buffer;
316 _WriterFix<rj::PrettyWriter<rj::StringBuffer>> writer(buffer);
317 writer.SetFormatOptions(rj::kFormatSingleLineArray);
318 ivalue.Accept(writer);
319
320 return buffer.GetString();
321 }
322
323
324 void
JsWriteValue(JsWriter * writer,const JsValue & js)325 JsWriteValue(JsWriter* writer, const JsValue& js)
326 {
327 if (!writer) {
328 return;
329 }
330
331 if (js.IsObject()) {
332 const JsObject& obj = js.GetJsObject();
333 writer->BeginObject();
334 for (const JsObject::value_type& field : obj) {
335 writer->WriteKey(field.first);
336 JsWriteValue(writer, field.second);
337 }
338 writer->EndObject();
339 } else if (js.IsArray()) {
340 const JsArray& array = js.GetJsArray();
341 writer->BeginArray();
342 for (const JsValue& elem : array) {
343 JsWriteValue(writer, elem);
344 }
345 writer->EndArray();
346 } else if (js.IsUInt64()) {
347 writer->WriteValue(js.GetUInt64());
348 } else if (js.IsString()) {
349 writer->WriteValue(js.GetString());
350 } else if (js.IsBool()) {
351 writer->WriteValue(js.GetBool());
352 } else if (js.IsReal()) {
353 writer->WriteValue(js.GetReal());
354 } else if (js.IsInt()) {
355 writer->WriteValue(js.GetInt64());
356 } else if (js.IsNull()) {
357 writer->WriteValue(nullptr);
358 }
359 }
360
361 //
362 // JsWriter
363 //
364
365 namespace {
366
367 // This helper interface is to wrap rapidJSON Writer and PrettyWriter so we can
368 // choose which writer to use at runtime.
369 class Js_PolymorphicWriterInterface
370 {
371 public:
372 virtual ~Js_PolymorphicWriterInterface();
373 virtual bool Null () = 0;
374 virtual bool Bool (bool b) = 0;
375 virtual bool Int (int i) = 0;
376 virtual bool Uint (unsigned u) = 0;
377 virtual bool Int64 (int64_t i64) = 0;
378 virtual bool Uint64 (uint64_t u64) = 0;
379 virtual bool Double (double d) = 0;
380 virtual bool String (const char *str, size_t length) = 0;
381 virtual bool StartObject () = 0;
382 virtual bool Key (const char *str, size_t length) = 0;
383 virtual bool EndObject () = 0;
384 virtual bool StartArray () = 0;
385 virtual bool EndArray() = 0;
386 };
387
388 Js_PolymorphicWriterInterface::~Js_PolymorphicWriterInterface() = default;
389
390 // Wraps the rapidJSON class and exposes its interface via virtual functions.
391 template <class TWriter>
392 class Js_PolymorphicWriter : public Js_PolymorphicWriterInterface, public TWriter
393 {
394 public:
395 using Writer = TWriter;
396 using Writer::Writer;
397
Null()398 bool Null () override {
399 return Writer::Null();
400 }
Bool(bool b)401 bool Bool (bool b) override {
402 return Writer::Bool(b);
403 }
Int(int i)404 bool Int (int i) override {
405 return Writer::Int(i);
406 }
Uint(unsigned u)407 bool Uint (unsigned u) override {
408 return Writer::Uint(u);
409 }
Int64(int64_t i64)410 bool Int64 (int64_t i64) override {
411 return Writer::Int64(i64);
412 }
Uint64(uint64_t u64)413 bool Uint64 (uint64_t u64) override {
414 return Writer::Uint64(u64);
415 }
Double(double d)416 bool Double (double d) override {
417 return Writer::Double(d);
418 }
String(const char * str,size_t length)419 bool String (const char *str, size_t length) override {
420 return Writer::String(str, length);
421 }
StartObject()422 bool StartObject () override {
423 return Writer::StartObject();
424 }
Key(const char * str,size_t length)425 bool Key (const char *str, size_t length) override {
426 return Writer::Key(str, length);
427 }
EndObject()428 bool EndObject () override {
429 return Writer::EndObject();
430 }
StartArray()431 bool StartArray () override {
432 return Writer::StartArray();
433 }
EndArray()434 bool EndArray() override {
435 return Writer::EndArray();
436 }
437 };
438
439 }
440
441 // JsWriter is a wrapper around a Js_PolymorphicWriterInterface instance.
442 class JsWriter::_Impl
443 {
444 using PrettyWriter =
445 Js_PolymorphicWriter<_WriterFix<rj::PrettyWriter<rj::OStreamWrapper>>>;
446 using Writer =
447 Js_PolymorphicWriter<_WriterFix<rj::Writer<rj::OStreamWrapper>>>;
448
449 public:
_Impl(std::ostream & s,Style style)450 _Impl(std::ostream& s, Style style)
451 : _strWrapper(s) {
452 switch (style) {
453 case Style::Compact:
454 _writer = std::unique_ptr<Writer>(new Writer(_strWrapper));
455 break;
456 case Style::Pretty:
457 _writer = std::unique_ptr<PrettyWriter>(
458 new PrettyWriter(_strWrapper));
459 break;
460 }
461 }
462
GetWriter()463 Js_PolymorphicWriterInterface* GetWriter() {
464 return _writer.get();
465 }
466
467 private:
468 std::unique_ptr<Js_PolymorphicWriterInterface> _writer;
469 rj::OStreamWrapper _strWrapper;
470 };
471
JsWriter(std::ostream & ostr,Style style)472 JsWriter::JsWriter(std::ostream& ostr, Style style)
473 : _impl(new _Impl(ostr, style))
474 {
475
476 }
477
478 JsWriter::~JsWriter() = default;
479
WriteValue(std::nullptr_t)480 bool JsWriter::WriteValue(std::nullptr_t)
481 {
482 return _impl->GetWriter()->Null();
483 }
484
WriteValue(bool b)485 bool JsWriter::WriteValue(bool b )
486 {
487 return _impl->GetWriter()->Bool(b);
488 }
489
WriteValue(int i)490 bool JsWriter::WriteValue(int i )
491 {
492 return _impl->GetWriter()->Int(i);
493 }
494
WriteValue(unsigned u)495 bool JsWriter::WriteValue(unsigned u )
496 {
497 return _impl->GetWriter()->Uint(u);
498 }
499
WriteValue(int64_t i64)500 bool JsWriter::WriteValue(int64_t i64 )
501 {
502 return _impl->GetWriter()->Int64(i64);
503 }
504
WriteValue(uint64_t u64)505 bool JsWriter::WriteValue(uint64_t u64 )
506 {
507 return _impl->GetWriter()->Uint64(u64);
508 }
509
WriteValue(double d)510 bool JsWriter::WriteValue(double d )
511 {
512 return _impl->GetWriter()->Double(d);
513 }
514
WriteValue(const std::string & s)515 bool JsWriter::WriteValue(const std::string& s)
516 {
517 return _impl->GetWriter()->String(s.c_str(), s.length());
518 }
519
WriteValue(const char * s)520 bool JsWriter::WriteValue(const char* s)
521 {
522 return _impl->GetWriter()->String(s, strlen(s));
523 }
524
BeginObject()525 bool JsWriter::BeginObject( )
526 {
527 return _impl->GetWriter()->StartObject();
528 }
529
WriteKey(const std::string & k)530 bool JsWriter::WriteKey(const std::string& k)
531 {
532 return _impl->GetWriter()->Key(k.c_str(), k.length());
533 }
534
WriteKey(const char * k)535 bool JsWriter::WriteKey(const char* k)
536 {
537 return _impl->GetWriter()->Key(k, strlen(k));
538 }
539
_Key(const char * s,size_t len)540 bool JsWriter::_Key(const char* s, size_t len)
541 {
542 return _impl->GetWriter()->Key(s, len);
543 }
544
_String(const char * s,size_t len)545 bool JsWriter::_String(const char* s, size_t len)
546 {
547 return _impl->GetWriter()->String(s, len);
548 }
549
EndObject()550 bool JsWriter::EndObject( )
551 {
552 return _impl->GetWriter()->EndObject();
553 }
554
BeginArray()555 bool JsWriter::BeginArray( )
556 {
557 return _impl->GetWriter()->StartArray();
558 }
559
EndArray()560 bool JsWriter::EndArray( )
561 {
562 return _impl->GetWriter()->EndArray();
563 }
564
565 PXR_NAMESPACE_CLOSE_SCOPE
566