json_serializer.h Source File

CPP API: json_serializer.h Source File
json_serializer.h
Go to the documentation of this file.
1 /*
2 * Copyright (C) 2020-2026 MEmilio
3 *
4 * Authors: Daniel Abele
5 *
6 * Contact: Martin J. Kuehn <Martin.Kuehn@DLR.de>
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20 #ifndef MIO_IO_JSON_SERIALIZER_H
21 #define MIO_IO_JSON_SERIALIZER_H
22 
23 #include "memilio/config.h" // IWYU pragma: keep
24 #include <boost/function_types/components.hpp>
25 #include <type_traits>
26 
27 #ifdef MEMILIO_HAS_JSONCPP
28 
29 #include "memilio/io/io.h"
32 
33 #include "json/json.h" // IWYU pragma: keep
34 
35 #include <fstream>
36 #include <limits>
37 #include <utility>
38 
39 namespace mio
40 {
41 
51 template <class T>
52 struct JsonType : std::false_type {
53 };
54 //bool
55 template <>
56 struct JsonType<bool> : std::true_type {
57  static IOResult<bool> transform(const Json::Value& js)
58  {
59  if (js.isBool()) {
60  return success(js.asBool());
61  }
62  return failure(StatusCode::InvalidType, "Json value is not a bool.");
63  }
64  static Json::Value transform(bool b)
65  {
66  return {b};
67  }
68 };
69 // all integers less than 32 bit must be stored as int
70 
71 //signed small ints
72 template <class T>
73  requires(is_small_integral<T>::value && std::is_signed_v<T>)
74 struct JsonType<T> : std::true_type {
75  static IOResult<T> transform(const Json::Value& js)
76  {
77  if (js.isInt()) {
78  int i = js.asInt();
79  if (i >= int(std::numeric_limits<T>::min()) && i <= int(std::numeric_limits<T>::max())) {
80  return success(T(i));
81  }
82  return failure(StatusCode::OutOfRange, "Json value is not in range of the requested integer type.");
83  }
84  return failure(StatusCode::InvalidType, "Json value is not an integer.");
85  }
86  static Json::Value transform(T i)
87  {
88  return Json::Value(Json::Int(i));
89  }
90 };
91 //unsigned small ints
92 template <class T>
93  requires(is_small_integral<T>::value && std::is_unsigned_v<T>)
94 struct JsonType<T> : std::true_type {
95  static IOResult<T> transform(const Json::Value& js)
96  {
97  if (js.isUInt()) {
98  unsigned int i = js.asUInt();
99  if (i <= (unsigned int)std::numeric_limits<T>::max()) {
100  return success(T(i));
101  }
103  "Json value is not in range of the requested unsigned integer type.");
104  }
105  return failure(StatusCode::InvalidType, "Json value is not an unsigned integer.");
106  }
107  static Json::Value transform(T i)
108  {
109  return Json::Value(Json::UInt(i));
110  }
111 };
112 //signed big ints
113 template <class T>
114  requires(is_64bit_integral<T>::value && std::is_signed_v<T>)
115 struct JsonType<T> : std::true_type {
116  static IOResult<T> transform(const Json::Value& js)
117  {
118  if (js.isInt64()) {
119  return success(js.asInt64());
120  }
121  return failure(StatusCode::InvalidType, "Json value is not a 64 bit integer.");
122  }
123  static Json::Value transform(T i)
124  {
125  return {Json::Int64(i)};
126  }
127 };
128 //unsigned big ints
129 template <class T>
130  requires(is_64bit_integral<T>::value && std::is_unsigned_v<T>)
131 struct JsonType<T> : std::true_type {
132  static IOResult<T> transform(const Json::Value& js)
133  {
134  if (js.isUInt64()) {
135  return success(js.asUInt64());
136  }
137  return failure(StatusCode::InvalidType, "Json value is not an unsigned 64bit integer.");
138  }
139  static Json::Value transform(T i)
140  {
141  return {Json::UInt64(i)};
142  }
143 };
144 //double
145 template <>
146 struct JsonType<double> : std::true_type {
147 
148  static IOResult<double> transform(const Json::Value& js)
149  {
150  if (js.isDouble()) {
151  return success(js.asDouble());
152  }
153  return failure(StatusCode::InvalidType, "Json value is not a double.");
154  }
155  static Json::Value transform(double d)
156  {
157  return {d};
158  }
159 };
160 //float
161 template <>
162 struct JsonType<float> : std::true_type {
163  static IOResult<float> transform(const Json::Value& js)
164  {
165  if (js.isDouble()) {
166  auto d = js.asDouble();
167  if (d > double(std::numeric_limits<float>::max()) || d < -double(std::numeric_limits<float>::max())) {
168  return failure(StatusCode::OutOfRange, "Json value is not in single precision floating point range.");
169  }
170  return success(float(d));
171  }
172  return failure(StatusCode::InvalidType, "Json value is not a float.");
173  }
174  static Json::Value transform(float f)
175  {
176  return {double(f)};
177  }
178 };
179 //string
180 template <>
181 struct JsonType<std::string> : std::true_type {
182  static IOResult<std::string> transform(const Json::Value& js)
183  {
184  if (js.isString()) {
185  return success(js.asString());
186  }
187  return failure(StatusCode::InvalidType, "Json value is not a string.");
188  }
189  static Json::Value transform(const std::string& s)
190  {
191  return {s};
192  }
193 };
194 //string literals
195 template <>
196 struct JsonType<const char*> : std::true_type {
197  //cannot be read, but may be written (e.g. string literal), so only one way transform
198  static Json::Value transform(const char* s)
199  {
200  return {s};
201  }
202 };
206 template <class T>
207 concept IsJsonType = JsonType<T>::value;
208 
212 class JsonObject : public SerializerBase
213 {
214 public:
221  JsonObject(Json::Value& value, const std::shared_ptr<IOStatus>& status, int flags)
222  : SerializerBase{status, flags}
223  , m_value{value}
224  {
225  }
226 
234  template <class T>
235  requires(IsJsonType<T>)
236  void add_element(const std::string& name, const T& value);
237  template <class T>
238  requires(!IsJsonType<T>)
239  void add_element(const std::string& name, const T& value);
248  template <class T>
249  void add_optional(const std::string& name, const T* value);
250 
258  template <class Iter>
259  void add_list(const std::string& name, Iter b, Iter e);
260 
269  template <class T>
270  requires(IsJsonType<T>)
271  IOResult<T> expect_element(const std::string& name, Tag<T> tag) const;
272  template <class T>
273  requires(!IsJsonType<T>)
274  IOResult<T> expect_element(const std::string& name, Tag<T> tag) const;
285  template <class T>
286  IOResult<boost::optional<T>> expect_optional(const std::string& name, mio::Tag<T> tag);
287 
295  template <class T>
296  IOResult<std::vector<T>> expect_list(const std::string& name, Tag<T> tag);
297 
301  const auto& value() const&
302  {
303  return m_value;
304  }
305 
306 private:
307  Json::Value& m_value;
308 };
309 
313 class JsonContext : public SerializerBase
314 {
315 public:
322  JsonContext(const std::shared_ptr<IOStatus>& status, int flags)
323  : SerializerBase{status, flags}
324  , m_value{}
325  {
326  }
327 
335  JsonContext(Json::Value value, const std::shared_ptr<IOStatus>& status, int flags)
336  : SerializerBase(status, flags)
337  , m_value(std::move(value))
338  {
339  }
340 
348  JsonObject create_object(const std::string& type)
349  {
350  mio::unused(type);
351  m_value = Json::Value(Json::objectValue);
352  return {m_value, m_status, m_flags};
353  }
354 
362  JsonObject expect_object(const std::string& type)
363  {
364  mio::unused(type);
365  if (!m_value.isObject()) {
366  *m_status = IOStatus(StatusCode::InvalidType, "Json value must be an object.");
367  }
368  return {m_value, m_status, m_flags};
369  }
370 
375  const Json::Value& value() const&
376  {
377  return m_value;
378  }
382  Json::Value&& value() &&
383  {
384  return std::move(m_value);
385  }
395  template <class T>
396  requires(IsJsonType<T>)
397  friend void serialize_internal(JsonContext& io, const T& t)
398  {
399  io.m_value = JsonType<T>::transform(t);
400  }
401 
409  template <class T>
410  requires(IsJsonType<T>)
411  friend IOResult<T> deserialize_internal(JsonContext& io, Tag<T>)
412  {
413  return JsonType<T>::transform(io.m_value);
414  }
415 
420  template <IsContainer Container>
421  requires(!IsJsonType<Container> && !HasDeserialize<Container, JsonContext>)
422  friend void serialize_internal(JsonContext& io, const Container& v)
423  {
424  if (io.m_status->is_ok()) {
425  auto array = Json::Value(Json::arrayValue);
426  using std::begin;
427  using std::end;
428  for (auto it = begin(v); it != end(v); ++it) {
429  auto ctxt = JsonContext(io.m_status, io.m_flags);
430  mio::serialize(ctxt, *it);
431  if (io.m_status) {
432  array.append(std::move(ctxt).value());
433  }
434  }
435  io.m_value = std::move(array);
436  }
437  }
438 
443  template <IsContainer Container>
444  requires(!IsJsonType<Container> && !HasDeserialize<Container, JsonContext>)
445  friend IOResult<Container> deserialize_internal(JsonContext& io, Tag<Container>)
446  {
447  const auto& array = io.m_value;
448  if (array.isArray()) {
449  Container v;
450  for (auto&& el : array) {
451  auto ctxt = JsonContext(el, io.m_status, io.m_flags);
452  BOOST_OUTCOME_TRY(auto&& val, mio::deserialize(ctxt, Tag<typename Container::value_type>{}));
453  v.insert(v.end(), val);
454  }
455  return success(std::move(v));
456  }
457  return failure(StatusCode::InvalidType, "Json value must be an array.");
458  }
459 
460 private:
461  Json::Value m_value;
462 };
463 
470 inline IOResult<void> write_json(const std::string& path, const Json::Value& js_value)
471 {
472  std::ofstream ofs(path);
473  if (ofs.is_open()) {
474  Json::StreamWriterBuilder swb;
475  auto js_writer = std::unique_ptr<Json::StreamWriter>(swb.newStreamWriter());
476  js_writer->write(js_value, &ofs);
477  if (ofs) {
478  return success();
479  }
480  else {
481  return failure(StatusCode::UnknownError, "Unknown error writing json.");
482  }
483  }
484  else {
485  return failure(StatusCode::FileNotFound, path);
486  }
487 }
488 
496 template <class T>
497 IOResult<Json::Value> serialize_json(const T& t, int flags = IOF_None)
498 {
499  JsonContext ctxt{std::make_shared<IOStatus>(), flags};
500  serialize(ctxt, t);
501  if (!ctxt.status()) {
502  return failure(ctxt.status());
503  }
504  return success(ctxt.value());
505 }
506 
515 template <class T>
516 IOResult<void> write_json(const std::string& path, const T& t, int flags = IOF_None)
517 {
518  BOOST_OUTCOME_TRY(auto&& js, serialize_json(t, flags));
519  return write_json(path, js);
520 }
521 
527 inline IOResult<Json::Value> read_json(const std::string& path)
528 {
529  std::ifstream ifs(path);
530  if (ifs.is_open()) {
531  Json::CharReaderBuilder crb;
532  std::string err_msg;
533  Json::Value js_value;
534  if (Json::parseFromStream(crb, ifs, &js_value, &err_msg)) {
535  return success(js_value);
536  }
537  return failure(StatusCode::UnknownError, path + ", " + err_msg);
538  }
539  return failure(StatusCode::FileNotFound, path);
540 }
541 
550 template <class T>
551 IOResult<T> deserialize_json(const Json::Value& js, Tag<T> tag, int flags = IOF_None)
552 {
553  JsonContext ctxt{js, std::make_shared<IOStatus>(), flags};
554  return deserialize(static_cast<JsonContext&>(ctxt), tag);
555 }
556 
565 template <class T>
566 IOResult<T> read_json(const std::string& path, Tag<T> tag, int flags = IOF_None)
567 {
568  BOOST_OUTCOME_TRY(auto&& js, read_json(path));
569  return deserialize_json(js, tag, flags);
570 }
571 
573 //Implementations for JsonContext/Object member functions below//
575 
576 template <class T>
577  requires(IsJsonType<T>)
578 void JsonObject::add_element(const std::string& name, const T& value)
579 {
580  if (m_status->is_ok()) {
581  m_value[name] = JsonType<T>::transform(value);
582  }
583 }
584 
585 template <class T>
586  requires(!IsJsonType<T>)
587 void JsonObject::add_element(const std::string& name, const T& value)
588 {
589  if (m_status->is_ok()) {
590  auto ctxt = JsonContext(m_status, m_flags);
591  mio::serialize(ctxt, value);
592  if (m_status) {
593  m_value[name] = std::move(ctxt).value();
594  }
595  }
596 }
597 
598 template <class T>
599 void JsonObject::add_optional(const std::string& name, const T* value)
600 {
601  if (value) {
602  add_element(name, *value);
603  }
604 }
605 
606 template <class Iter>
607 void JsonObject::add_list(const std::string& name, Iter b, Iter e)
608 {
609  if (m_status->is_ok()) {
610  auto array = Json::Value(Json::arrayValue);
611  for (auto it = b; it != e; ++it) {
612  auto ctxt = JsonContext(m_status, m_flags);
613  mio::serialize(ctxt, *it);
614  if (m_status) {
615  array.append(std::move(ctxt).value());
616  }
617  }
618  m_value[name] = std::move(array);
619  }
620 }
621 
622 template <class T>
623  requires(IsJsonType<T>)
624 IOResult<T> JsonObject::expect_element(const std::string& name, Tag<T> /*tag*/) const
625 {
626  if (m_status->is_error()) {
627  return failure(*m_status);
628  }
629 
630  const auto& element = m_value[name];
631  if (element.isNull()) {
632  return failure(StatusCode::KeyNotFound, name);
633  }
634  auto r = JsonType<T>::transform(element);
635  if (r) {
636  return r;
637  }
638 
639  return failure(r.error().code(),
640  r.error().message() + " (" + name + ")"); //annotate type error message with element name
641 }
642 
643 template <class T>
644  requires(!IsJsonType<T>)
645 IOResult<T> JsonObject::expect_element(const std::string& name, Tag<T> tag) const
646 {
647  if (m_status->is_error()) {
648  return failure(*m_status);
649  }
650  const auto& element = m_value[name];
651  if (!element.isNull()) {
652  auto ctxt = JsonContext(element, m_status, m_flags);
653  auto r = mio::deserialize(ctxt, tag);
654  return r;
655  }
656  return failure(IOStatus{StatusCode::KeyNotFound, name});
657 }
658 
659 template <class T>
660 IOResult<boost::optional<T>> JsonObject::expect_optional(const std::string& name, mio::Tag<T> tag)
661 {
662  if (m_status->is_error()) {
663  return failure(*m_status);
664  }
665  const auto& element = m_value[name];
666  if (element.isNull()) {
667  return success(boost::optional<T>{});
668  }
669  auto r = expect_element(name, tag);
670  if (r) {
671  return success(r.value());
672  }
673  return failure(r.error());
674 }
675 
676 template <class T>
677 IOResult<std::vector<T>> JsonObject::expect_list(const std::string& name, Tag<T> tag)
678 {
679  if (m_status->is_error()) {
680  return failure(*m_status);
681  }
682  const auto& array = m_value[name];
683  if (array.isArray()) {
684  std::vector<T> v;
685  v.reserve(array.size());
686  for (auto&& el : array) {
687  auto ctxt = JsonContext(el, m_status, m_flags);
688  auto r = mio::deserialize(ctxt, tag);
689  if (r) {
690  v.emplace_back(r.value());
691  }
692  else {
693  return failure(r.error());
694  }
695  }
696  return success(std::move(v));
697  }
698  return failure(StatusCode::KeyNotFound, name);
699 }
700 
701 } // namespace mio
702 
703 #endif // MEMILIO_HAS_JSONCPP
704 
705 #endif // MIO_IO_JSON_SERIALIZER_H
static min_max_return_type< ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 >, ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > >::type min(const ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > &a, const ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > &b)
Definition: ad.hpp:2599
static min_max_return_type< ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 >, ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > >::type max(const ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > &a, const ad::internal::active_type< AD_TAPE_REAL, DATA_HANDLER_1 > &b)
Definition: ad.hpp:2596
trait_value< T >::RETURN_TYPE & value(T &x)
Definition: ad.hpp:3308
A collection of classes to simplify handling of matrix shapes in meta programming.
Definition: models/abm/analyze_result.h:30
Tag< Container >
Definition: io.h:807
requires details::IsElementReference< M > RowMajorIterator< M, false > end(M &m)
create a non-const end iterator for the matrix m.
Definition: eigen_util.h:449
IOResult< T > deserialize(IOContext &io, Tag< T > tag)
Restores an object from the data stored in an IO context.
Definition: io.h:860
auto failure(const IOStatus &s)
Create an object that is implicitly convertible to an error IOResult<T>.
Definition: io.h:380
void serialize(IOContext &io, const T &t)
Save data that describes an object in a format determined by the given context.
Definition: io.h:836
boost::outcome_v2::in_place_type_t< T > Tag
Type that is used for overload resolution.
Definition: io.h:407
auto i
Definition: io.h:809
requires(!std::is_trivial_v< T >) void BinarySerializerObject
Definition: binary_serializer.h:333
auto success()
Create an object that is implicitly convertible to a succesful IOResult<void>.
Definition: io.h:359
void unused(T &&...)
Does nothing, can be used to mark variables as not used.
Definition: compiler_diagnostics.h:30
requires details::IsElementReference< M > RowMajorIterator< M, false > begin(M &m)
create a non-const iterator to first element of the matrix m.
Definition: eigen_util.h:421
obj add_list("Items", container.begin(), container.end())
@ IOF_None
default behavior.
Definition: io.h:70
IOResult< T > deserialize_internal(IOContext &io, Tag< T > tag)
Deserialization implementation for the default serialization feature.
Definition: default_serialize.h:236
void serialize_internal(IOContext &io, const T &a)
Serialization implementation for the default serialization feature.
Definition: default_serialize.h:213
Definition: io.h:94