Zserio C++17 runtime library  0.5.0
Built for Zserio 2.17.0
JsonWriter.h
Go to the documentation of this file.
1 #ifndef ZSERIO_JSON_WRITER_H_INC
2 #define ZSERIO_JSON_WRITER_H_INC
3 
4 #include <ostream>
5 #include <string_view>
6 
8 #include "zserio/IWalkObserver.h"
9 #include "zserio/JsonEncoder.h"
10 #include "zserio/Optional.h"
11 #include "zserio/String.h"
12 #include "zserio/TypeInfoUtil.h"
13 
14 namespace zserio
15 {
16 using namespace std::literals::string_view_literals;
17 
21 template <typename ALLOC = std::allocator<uint8_t>>
22 class BasicJsonWriter : public IBasicWalkObserver<ALLOC>, public AllocatorHolder<ALLOC>
23 {
24 public:
26 
30  static constexpr const char* DEFAULT_ITEM_SEPARATOR = ", ";
31 
35  static constexpr const char* DEFAULT_ITEM_SEPARATOR_WITH_INDENT = ",";
36 
40  static constexpr const char* DEFAULT_KEY_SEPARATOR = ": ";
41 
45  enum class EnumerableFormat
46  {
48  NUMBER,
65  STRING
66  };
70  static constexpr EnumerableFormat DEFAULT_ENUMERABLE_FORMAT = EnumerableFormat::STRING;
71 
78  explicit BasicJsonWriter(std::ostream& out, const ALLOC& allocator = ALLOC());
79 
87  BasicJsonWriter(std::ostream& out, uint8_t indent, const ALLOC& allocator = ALLOC());
88 
96  BasicJsonWriter(std::ostream& out, const BasicString<RebindAlloc<ALLOC, char>>& indent,
97  const ALLOC& allocator = ALLOC());
98 
102  ~BasicJsonWriter() override = default;
103 
108  BasicJsonWriter(const BasicJsonWriter& other) = delete;
109  BasicJsonWriter& operator=(const BasicJsonWriter& other) = delete;
110 
111  BasicJsonWriter(BasicJsonWriter&& other) = delete;
124  void setItemSeparator(const BasicString<RebindAlloc<ALLOC, char>>& itemSeparator);
125 
133  void setKeySeparator(const BasicString<RebindAlloc<ALLOC, char>>& keySeparator);
134 
140  void setEnumerableFormat(EnumerableFormat enumerableFormat);
141 
142  void beginRoot(const IBasicReflectableDataConstPtr<ALLOC>& compound) override;
143  void endRoot(const IBasicReflectableDataConstPtr<ALLOC>& compound) override;
144 
145  void beginArray(
146  const IBasicReflectableDataConstPtr<ALLOC>& array, const BasicFieldInfo<ALLOC>& fieldInfo) override;
147  void endArray(
148  const IBasicReflectableDataConstPtr<ALLOC>& array, const BasicFieldInfo<ALLOC>& fieldInfo) override;
149 
150  void beginCompound(const IBasicReflectableDataConstPtr<ALLOC>& compound,
151  const BasicFieldInfo<ALLOC>& fieldInfo, size_t elementIndex) override;
152  void endCompound(const IBasicReflectableDataConstPtr<ALLOC>& compound,
153  const BasicFieldInfo<ALLOC>& fieldInfo, size_t elementIndex) override;
154 
155  void visitValue(const IBasicReflectableDataConstPtr<ALLOC>& value, const BasicFieldInfo<ALLOC>& fieldInfo,
156  size_t elementIndex) override;
157 
158 private:
159  BasicJsonWriter(std::ostream& out, Optional<BasicString<RebindAlloc<ALLOC, char>>>&& optionalIndent,
160  const ALLOC& allocator = ALLOC());
161 
162  void beginItem();
163  void endItem();
164  void beginObject();
165  void endObject();
166  void beginArray();
167  void endArray();
168 
169  void writeIndent();
170  void writeKey(std::string_view key);
171  void writeValue(const IBasicReflectableDataConstPtr<ALLOC>& reflectable);
172  void writeBitBuffer(const BasicBitBuffer<ALLOC>& bitBuffer);
173  void writeBytes(Span<const uint8_t> value);
174  void writeStringifiedEnum(const IBasicReflectableDataConstPtr<ALLOC>& reflectable);
175  void writeStringifiedBitmask(const IBasicReflectableDataConstPtr<ALLOC>& reflectable);
176 
177  std::ostream& m_out;
179  BasicString<RebindAlloc<ALLOC, char>> m_itemSeparator;
180  BasicString<RebindAlloc<ALLOC, char>> m_keySeparator;
181  EnumerableFormat m_enumerableFormat = DEFAULT_ENUMERABLE_FORMAT;
182 
183  bool m_isFirst = true;
184  size_t m_level = 0;
185 };
186 
192 template <typename ALLOC>
193 BasicJsonWriter<ALLOC>::BasicJsonWriter(std::ostream& out, const ALLOC& allocator) :
194  BasicJsonWriter(out, std::nullopt, allocator)
195 {}
196 
197 template <typename ALLOC>
198 BasicJsonWriter<ALLOC>::BasicJsonWriter(std::ostream& out, uint8_t indent, const ALLOC& allocator) :
199  BasicJsonWriter(out, BasicString<RebindAlloc<ALLOC, char>>(indent, ' ', allocator), allocator)
200 {}
201 
202 template <typename ALLOC>
204  std::ostream& out, const BasicString<RebindAlloc<ALLOC, char>>& indent, const ALLOC& allocator) :
205  BasicJsonWriter(out, Optional<BasicString<RebindAlloc<ALLOC, char>>>(indent), allocator)
206 {}
207 
208 template <typename ALLOC>
210  Optional<BasicString<RebindAlloc<ALLOC, char>>>&& optionalIndent, const ALLOC& allocator) :
211  AllocatorHolder<ALLOC>(allocator),
212  m_out(out),
213  m_indent(optionalIndent),
214  m_itemSeparator(
215  m_indent.has_value() ? DEFAULT_ITEM_SEPARATOR_WITH_INDENT : DEFAULT_ITEM_SEPARATOR, allocator),
216  m_keySeparator(DEFAULT_KEY_SEPARATOR, allocator)
217 {}
218 
219 template <typename ALLOC>
221 {
222  m_itemSeparator = itemSeparator;
223 }
224 
225 template <typename ALLOC>
227 {
228  m_keySeparator = keySeparator;
229 }
230 
231 template <typename ALLOC>
233 {
234  m_enumerableFormat = enumerableFormat;
235 }
236 
237 template <typename ALLOC>
239 {
240  beginObject();
241 }
242 
243 template <typename ALLOC>
245 {
246  endObject();
247  m_out.flush();
248 }
249 
250 template <typename ALLOC>
253 {
254  beginItem();
255 
256  writeKey(fieldInfo.schemaName);
257 
258  beginArray();
259 }
260 
261 template <typename ALLOC>
263 {
264  endArray();
265 
266  endItem();
267 }
268 
269 template <typename ALLOC>
271  const BasicFieldInfo<ALLOC>& fieldInfo, size_t elementIndex)
272 {
273  beginItem();
274 
275  if (elementIndex == WALKER_NOT_ELEMENT)
276  {
277  writeKey(fieldInfo.schemaName);
278  }
279 
280  beginObject();
281 }
282 
283 template <typename ALLOC>
286 {
287  endObject();
288 
289  endItem();
290 }
291 
292 template <typename ALLOC>
294  const BasicFieldInfo<ALLOC>& fieldInfo, size_t elementIndex)
295 {
296  beginItem();
297 
298  if (elementIndex == WALKER_NOT_ELEMENT)
299  {
300  writeKey(fieldInfo.schemaName);
301  }
302 
303  writeValue(value);
304 
305  endItem();
306 }
307 
308 template <typename ALLOC>
310 {
311  if (!m_isFirst)
312  {
313  m_out.write(m_itemSeparator.data(), static_cast<std::streamsize>(m_itemSeparator.size()));
314  }
315 
316  if (m_indent.has_value())
317  {
318  m_out.put('\n');
319  }
320 
321  writeIndent();
322 }
323 
324 template <typename ALLOC>
325 void BasicJsonWriter<ALLOC>::endItem()
326 {
327  m_isFirst = false;
328 }
329 
330 template <typename ALLOC>
331 void BasicJsonWriter<ALLOC>::beginObject()
332 {
333  m_out.put('{');
334 
335  m_isFirst = true;
336  m_level += 1;
337 }
338 
339 template <typename ALLOC>
340 void BasicJsonWriter<ALLOC>::endObject()
341 {
342  if (m_indent.has_value())
343  {
344  m_out.put('\n');
345  }
346 
347  m_level -= 1;
348 
349  writeIndent();
350 
351  m_out.put('}');
352 }
353 
354 template <typename ALLOC>
355 void BasicJsonWriter<ALLOC>::beginArray()
356 {
357  m_out.put('[');
358 
359  m_isFirst = true;
360  m_level += 1;
361 }
362 
363 template <typename ALLOC>
364 void BasicJsonWriter<ALLOC>::endArray()
365 {
366  if (m_indent.has_value())
367  {
368  m_out.put('\n');
369  }
370 
371  m_level -= 1;
372 
373  writeIndent();
374 
375  m_out.put(']');
376 }
377 
378 template <typename ALLOC>
379 void BasicJsonWriter<ALLOC>::writeIndent()
380 {
381  if (m_indent.has_value())
382  {
383  const auto& indent = m_indent.value();
384  if (!indent.empty())
385  {
386  for (size_t i = 0; i < m_level; ++i)
387  {
388  m_out.write(indent.data(), static_cast<std::streamsize>(indent.size()));
389  }
390  }
391  }
392 }
393 
394 template <typename ALLOC>
395 void BasicJsonWriter<ALLOC>::writeKey(std::string_view key)
396 {
397  JsonEncoder::encodeString(m_out, key);
398  m_out.write(m_keySeparator.data(), static_cast<std::streamsize>(m_keySeparator.size()));
399  m_out.flush();
400 }
401 
402 template <typename ALLOC>
403 void BasicJsonWriter<ALLOC>::writeValue(const IBasicReflectableDataConstPtr<ALLOC>& reflectable)
404 {
405  if (!reflectable)
406  {
408  return;
409  }
410 
411  const IBasicTypeInfo<ALLOC>& typeInfo = reflectable->getTypeInfo();
412  switch (typeInfo.getCppType())
413  {
414  case CppType::BOOL:
415  JsonEncoder::encodeBool(m_out, reflectable->getBool());
416  break;
417  case CppType::INT8:
418  case CppType::INT16:
419  case CppType::INT32:
420  case CppType::INT64:
421  JsonEncoder::encodeIntegral(m_out, reflectable->toInt());
422  break;
423  case CppType::UINT8:
424  case CppType::UINT16:
425  case CppType::UINT32:
426  case CppType::UINT64:
427  JsonEncoder::encodeIntegral(m_out, reflectable->toUInt());
428  break;
429  case CppType::FLOAT:
430  JsonEncoder::encodeFloatingPoint(m_out, static_cast<double>(reflectable->getFloat()));
431  break;
432  case CppType::DOUBLE:
433  JsonEncoder::encodeFloatingPoint(m_out, reflectable->getDouble());
434  break;
435  case CppType::BYTES:
436  writeBytes(reflectable->getBytes());
437  break;
438  case CppType::STRING:
439  JsonEncoder::encodeString(m_out, reflectable->getStringView());
440  break;
441  case CppType::BIT_BUFFER:
442  writeBitBuffer(reflectable->getBitBuffer());
443  break;
444  case CppType::ENUM:
445  if (m_enumerableFormat == EnumerableFormat::STRING)
446  {
447  writeStringifiedEnum(reflectable);
448  }
450  {
451  JsonEncoder::encodeIntegral(m_out, reflectable->toInt());
452  }
453  else
454  {
455  JsonEncoder::encodeIntegral(m_out, reflectable->toUInt());
456  }
457  break;
458  case CppType::BITMASK:
459  if (m_enumerableFormat == EnumerableFormat::STRING)
460  {
461  writeStringifiedBitmask(reflectable);
462  }
463  else
464  {
465  JsonEncoder::encodeIntegral(m_out, reflectable->toUInt());
466  }
467  break;
468  default:
469  throw CppRuntimeException("JsonWriter: Unexpected not-null value of type '")
470  << typeInfo.getSchemaName() << "'!";
471  }
472 
473  m_out.flush();
474 }
475 
476 template <typename ALLOC>
477 void BasicJsonWriter<ALLOC>::writeBitBuffer(const BasicBitBuffer<ALLOC>& bitBuffer)
478 {
479  beginObject();
480  beginItem();
481  writeKey("buffer"sv);
482  beginArray();
483  Span<const uint8_t> buffer = bitBuffer.getData();
484  for (uint8_t element : buffer)
485  {
486  beginItem();
487  JsonEncoder::encodeIntegral(m_out, element);
488  endItem();
489  }
490  endArray();
491  endItem();
492  beginItem();
493  writeKey("bitSize"sv);
494  JsonEncoder::encodeIntegral(m_out, bitBuffer.getBitSize());
495  endItem();
496  endObject();
497 }
498 
499 template <typename ALLOC>
500 void BasicJsonWriter<ALLOC>::writeBytes(Span<const uint8_t> value)
501 {
502  beginObject();
503  beginItem();
504  writeKey("buffer"sv);
505  beginArray();
506  for (uint8_t byte : value)
507  {
508  beginItem();
509  JsonEncoder::encodeIntegral(m_out, byte);
510  endItem();
511  }
512  endArray();
513  endItem();
514  endObject();
515 }
516 
517 template <typename ALLOC>
518 void BasicJsonWriter<ALLOC>::writeStringifiedEnum(const IBasicReflectableDataConstPtr<ALLOC>& reflectable)
519 {
520  const auto& typeInfo = reflectable->getTypeInfo();
521  const uint64_t enumValue = TypeInfoUtil::isSigned(typeInfo.getUnderlyingType().getCppType())
522  ? static_cast<uint64_t>(reflectable->toInt())
523  : reflectable->toUInt();
524  for (const auto& itemInfo : typeInfo.getEnumItems())
525  {
526  if (itemInfo.value == enumValue)
527  {
528  // exact match
529  JsonEncoder::encodeString(m_out, itemInfo.schemaName);
530  return;
531  }
532  }
533 
534  // no match
535  BasicString<RebindAlloc<ALLOC, char>> stringValue =
537  ? toString(reflectable->toInt(), get_allocator())
538  : toString(reflectable->toUInt(), get_allocator());
539  stringValue.append(" /* no match */");
540  JsonEncoder::encodeString(m_out, stringValue);
541 }
542 
543 template <typename ALLOC>
544 void BasicJsonWriter<ALLOC>::writeStringifiedBitmask(const IBasicReflectableDataConstPtr<ALLOC>& reflectable)
545 {
546  BasicString<RebindAlloc<ALLOC, char>> stringValue(get_allocator());
547  const auto& typeInfo = reflectable->getTypeInfo();
548  const uint64_t bitmaskValue = reflectable->toUInt();
549  uint64_t valueCheck = 0;
550  for (const auto& itemInfo : typeInfo.getBitmaskValues())
551  {
552  if ((itemInfo.value != 0 && (bitmaskValue & itemInfo.value) == itemInfo.value) ||
553  (itemInfo.value == 0 && bitmaskValue == 0))
554  {
555  valueCheck |= itemInfo.value;
556  if (!stringValue.empty())
557  {
558  stringValue += " | ";
559  }
560  stringValue += toString(itemInfo.schemaName, get_allocator());
561  }
562  }
563 
564  if (stringValue.empty())
565  {
566  // no match
567  stringValue.append(toString(bitmaskValue, get_allocator()));
568  stringValue.append(" /* no match */");
569  }
570  else if (bitmaskValue != valueCheck)
571  {
572  // partial match
573  stringValue =
574  toString(bitmaskValue, get_allocator())
575  .append(" /* partial match: ")
576  .append(stringValue)
577  .append(" */");
578  }
579  // else exact match
580 
581  JsonEncoder::encodeString(m_out, stringValue);
582 }
583 
584 } // namespace zserio
585 
586 #endif // ZSERIO_JSON_WRITER_H_INC
BasicJsonWriter(std::ostream &out, const ALLOC &allocator=ALLOC())
Definition: JsonWriter.h:193
BasicJsonWriter & operator=(const BasicJsonWriter &other)=delete
void endRoot(const IBasicReflectableDataConstPtr< ALLOC > &compound) override
Definition: JsonWriter.h:244
void setKeySeparator(const BasicString< RebindAlloc< ALLOC, char >> &keySeparator)
Definition: JsonWriter.h:226
BasicJsonWriter(BasicJsonWriter &&other)=delete
void visitValue(const IBasicReflectableDataConstPtr< ALLOC > &value, const BasicFieldInfo< ALLOC > &fieldInfo, size_t elementIndex) override
Definition: JsonWriter.h:293
void endCompound(const IBasicReflectableDataConstPtr< ALLOC > &compound, const BasicFieldInfo< ALLOC > &fieldInfo, size_t elementIndex) override
Definition: JsonWriter.h:284
~BasicJsonWriter() override=default
BasicJsonWriter(const BasicJsonWriter &other)=delete
void setItemSeparator(const BasicString< RebindAlloc< ALLOC, char >> &itemSeparator)
Definition: JsonWriter.h:220
void setEnumerableFormat(EnumerableFormat enumerableFormat)
Definition: JsonWriter.h:232
BasicJsonWriter & operator=(BasicJsonWriter &&other)=delete
void beginCompound(const IBasicReflectableDataConstPtr< ALLOC > &compound, const BasicFieldInfo< ALLOC > &fieldInfo, size_t elementIndex) override
Definition: JsonWriter.h:270
void beginRoot(const IBasicReflectableDataConstPtr< ALLOC > &compound) override
Definition: JsonWriter.h:238
virtual const IBasicTypeInfo< ALLOC > & getUnderlyingType() const =0
virtual CppType getCppType() const =0
virtual Span< const ItemInfo > getEnumItems() const =0
virtual Span< const ItemInfo > getBitmaskValues() const =0
virtual std::string_view getSchemaName() const =0
static void encodeFloatingPoint(std::ostream &stream, double value)
Definition: JsonEncoder.cpp:20
static void encodeNull(std::ostream &stream)
Definition: JsonEncoder.cpp:10
static void encodeBool(std::ostream &stream, bool value)
Definition: JsonEncoder.cpp:15
static void encodeIntegral(std::ostream &stream, T value)
Definition: JsonEncoder.h:64
static void encodeString(std::ostream &stream, std::string_view value)
Definition: JsonEncoder.cpp:50
Definition: BitBuffer.h:602
typename IBasicReflectableData< ALLOC >::ConstPtr IBasicReflectableDataConstPtr
std::basic_string< char, std::char_traits< char >, ALLOC > BasicString
Definition: String.h:17
IBasicReflectableDataConstPtr< ALLOC > reflectable(const T &value, const ALLOC &allocator=ALLOC())
typename std::allocator_traits< ALLOC >::template rebind_alloc< T > RebindAlloc
Definition: RebindAlloc.h:10
const IBasicTypeInfo< ALLOC > & typeInfo()
Definition: ITypeInfo.h:668
BasicString< RebindAlloc< ALLOC, char > > toString(T value, const ALLOC &allocator=ALLOC())
std::string_view schemaName
Definition: ITypeInfo.h:493
static bool isSigned(SchemaType schemaType)