6#include <rapidjson/prettywriter.h>
12Writ::InstanceMap::wrapper_to_json (Wrapper *wrapper,
const size_t thisid, Jsonipc::JsonAllocator &allocator)
14 warning (
"Ase::Writ: object pointer is not persistent: (%s*) {\"$id\":%d}", wrapper->classname(), thisid);
16 return this->Jsonipc::InstanceMap::wrapper_to_json (wrapper, thisid, allocator);
20Writ::InstanceMap::wrapper_from_json (
const Jsonipc::JsonValue &value)
23 warning (
"Ase::Writ: non persistent object cannot resolve: %s*", Jsonipc::jsonvalue_to_string (value));
25 return this->Jsonipc::InstanceMap::wrapper_from_json (value);
29Writ::Writ (
Flags flags) :
31 skip_zero_ (flags & SKIP_ZERO),
32 skip_emptystring_ (flags & SKIP_EMPTYSTRING),
33 relaxed_ (flags & RELAXED),
40 if (dummy_->index() != Value::NONE)
42 warning (
"invalid Writ::dummy assignment: %s", dummy_->repr());
54 root_.value_ = Value::empty_value;
64 rapidjson::Document document (rapidjson::kNullType);
65 Jsonipc::JsonValue &docroot = document;
66 Jsonipc::JsonAllocator &allocator = document.GetAllocator();
67 docroot = Jsonipc::to_json (root_.value_, allocator);
68 rapidjson::StringBuffer buffer;
71 constexpr unsigned FLAGS = rapidjson::kWriteNanAndInfFlag;
72 rapidjson::PrettyWriter<rapidjson::StringBuffer, rapidjson::UTF8<>, rapidjson::UTF8<>, rapidjson::CrtAllocator, FLAGS> writer (buffer);
73 writer.SetIndent (
' ', 2);
74 writer.SetFormatOptions (rapidjson::kFormatSingleLineArray);
75 document.Accept (writer);
79 rapidjson::Writer<rapidjson::StringBuffer> writer (buffer);
80 document.Accept (writer);
82 const String output { buffer.GetString(), buffer.GetSize() };
87Writ::from_json (
const String &jsonstring)
91 rapidjson::Document document;
92 Jsonipc::JsonValue &docroot = document;
93 constexpr unsigned PARSE_FLAGS =
94 rapidjson::kParseFullPrecisionFlag |
95 rapidjson::kParseCommentsFlag |
96 rapidjson::kParseTrailingCommasFlag |
97 rapidjson::kParseNanAndInfFlag |
98 rapidjson::kParseEscapedApostropheFlag;
99 document.Parse<PARSE_FLAGS> (jsonstring.
data(), jsonstring.
size());
100 if (document.HasParseError())
105 root_.value_ = Jsonipc::from_json<Value> (docroot);
110Writ::blank_enum (
const String &enumname)
112 warning (
"%s: serialization of enum type without values: %s",
"Writ::blank_enum", enumname);
116Writ::not_serializable (
const String &classname)
118 warning (
"%s: type not registered as Jsonipc::Serializable<>: %s",
"Writ::not_serializable", classname);
159 for (LinkEntry &e : links_)
160 if (e.sp == &serializable)
163 warning (
"Ase::Writ: duplicate serialization of: (%s)%p", Jsonipc::rtti_typename (serializable), &serializable);
167 links_.
push_back ({ valuep, &serializable, 0 });
175 for (LinkEntry &e : links_)
176 if (e.sp == &serializable)
179 e.id = ++link_counter_;
182 links_.
push_back ({
nullptr, &serializable, ++link_counter_ });
183 return links_.
back().id;
186static const char *
const ase_linkid =
"\u0012.ID";
193 for (LinkEntry &e : links_)
196 if (e.value && e.value->index() == Value::RECORD)
202 warning (
"Ase::Writ: missing serialization of: (%s)%p", Jsonipc::rtti_typename (*e.sp), e.sp);
211 links_.
push_back ({
nullptr, &serializable,
id });
216Writ::resolve_link (
int64 id)
219 for (LinkEntry &e : links_)
230 for (LinkPtr &e : linkptrs_)
234 *e.spp = serializable;
236 warning (
"Ase::Writ: failed to resolve serialization link: %d", e.id);
243 writ_ (writ), valuep_ (vp), value_ (*vp)
250WritNode::recfield (
const String &fieldname,
bool front)
254 assert_return (value_.index() == Value::RECORD, { writ_, dummy() });
256 return WritNode { writ_, rec.valuep (fieldname, front) };
258 if (
in_load() && value_.index() == Value::RECORD)
261 ValueP vp = rec.peek (fieldname);
277 for (
auto &valp : values)
289 if (value_.index() == Value::NONE)
290 value_ = ValueS::empty_array;
291 if (value_.index() == Value::ARRAY)
294 array.push_back (
Value());
295 return { writ_, array.
back() };
298 const WritNode fallback { writ_, dummy() };
312 const int64 linkid = writ_.use_link (**l.spp_);
324 writ_.linkptrs_.
push_back ({ l.spp_, linkid });
336 value_ = ValueR::empty_record;
337 writ_.prepare_link (serializable, valuep_);
342 if (value_.index() == Value::RECORD)
344 const int64 linkid = (*this)[ase_linkid].as_int();
346 writ_.collect_link (linkid, serializable);
360 if (purge_emptystring && field.value->index() == Value::STRING &&
std::get<String> (*field.value) ==
"")
362 if (purge_zero && field.value->index() == Value::INT64 &&
std::get<int64> (*field.value) == 0)
364 if (purge_zero && field.value->index() == Value::DOUBLE &&
std::get<double> (*field.value) == 0)
392struct Simple {
int i = 0;
float f = 0;
String s; };
394serialize (Simple &simple,
WritNode &xs)
420 chars.push_back (
'A');
421 chars.push_back (
'B');
422 chars.push_back (
'C');
423 tf = { .
name =
"NAME", .type =
"TYPE", .offset = -1234567, .
length = 987654321 };
440template<
typename T>
static T via_json (
const T &v) {
return json_parse<T> (
json_stringify (v)); }
447 TASSERT (
false == via_json (
false));
448 TASSERT (
true == via_json (
true));
450 TASSERT (+3.5 == via_json (+3.5));
453 TASSERT (Error::PERMS == via_json (Error::PERMS));
456 TASSERT (floats == via_json (floats2));
459 using namespace MakeIcon;
460 Choice choice = {
"grump",
"¿"_uc,
"Grump",
"A flashy Grump",
"Notice",
"Warn" }, choice2 = via_json (choice);
461 TASSERT (choice.
ident == choice2.ident && choice.
icon == choice2.icon && choice.
label == choice2.label &&
462 choice.
blurb == choice2.blurb && choice.
notice == choice2.notice && choice.
warning == choice2.warning);
466 Tuple tuple = {
"TUPLE", -618033988, 1.6180339887498948482 };
467 Tuple u = via_json (tuple);
472 Simple simple { 7, -3.14159265358979,
"SIMPLE" };
473 auto s2 = via_json (simple);
474 TASSERT (s2.i == 7 &&
fabs (s2.f - -3.14159265358979) < 1e-7 && s2.s ==
"SIMPLE");
506 bool operator== (
const Test1 &o)
const {
return i == o.i && t == o.t; }
509 test1 .
set (
"i", &Test1::i) .set (
"t", &Test1::t) ;
516 bool operator== (
const Test2 &o)
const {
517 return i == o.i && t1 == o.t1 && server == o.server && serverp == o.serverp;
524 .
set (
"i", &Test2::i)
525 .set (
"t1", &Test2::t1)
526 .set (
"server", &Test2::server)
529 Test2 t2, t2orig = Test2 ({ 777, { 9,
"nine" }, });
533 TASSERT (s == R
"'''({"i":777,"server":null,"t1":{"i":9,"t":"nine"}})'''");
542 TASSERT (sbase1.chars == sbase2.chars && sbase1.d == sbase2.d && sbase1.e == sbase2.e);
556 xs[
"flags"] & flags_;
559struct FrobnicatorSpecial :
public FrobnicatorBase {
563 explicit FrobnicatorSpecial (
const String &k) : kind_ (k)
565 TASSERT (kind_ ==
"K321" || kind_ ==
"Special");
574 FrobnicatorBase::serialize (xs);
577 xs[
"state"] & this->state_;
579 xs[
"factor"] & factor_;
583struct FrobnicatorImpl :
public FrobnicatorBase {
587 FrobnicatorSpecialP special_;
592 explicit FrobnicatorImpl (
const String &t =
"") : kind_ (t) {}
595 for (
auto *c : children_)
599 create_lane (
const String &kind)
602 if (kind ==
"Special")
603 c =
new FrobnicatorSpecial (kind);
605 c =
new FrobnicatorImpl (kind);
606 return *children_.emplace_back (c);
609 populate (
bool extended =
false)
614 special_->flags_ = 321;
618 matrix_ = { { 9, 8, 7 }, { 6, 5, 4 }, };
619 error_ = Error::NO_MEMORY;
629 FrobnicatorBase::serialize (xs);
633 xs[
"matrix"] & matrix_;
636 xs[
"error"] & error_;
641 xs[
"special"] & *special_;
644 for (
auto &childp : children_)
651 for (
auto &xc : xs[
"tracks"].to_nodes())
653 Serializable &child = create_lane (xc[
"kind"].as_string());
657 xs[
"sibling"] &
WritLink (&sibling_);
661 if (xs.
in_load() && xs.has (
"Q?"))
662 yesno_ =
yn ==
"yes";
668test_serializable_hierarchy()
673 FrobnicatorImpl prjct;
675 prjct.populate (
true);
676 TASSERT (prjct.children_.size() == 0);
677 auto &nidi = prjct.create_lane (
"Nidi");
678 dynamic_cast<FrobnicatorBase*
> (&nidi)->flags_ = 0x02020202;
679 prjct.sibling_ = &prjct.create_lane (
"Odio");
680 FrobnicatorImpl *fimpl =
dynamic_cast<FrobnicatorImpl*
> (prjct.sibling_);
681 fimpl->populate (
false);
682 fimpl->yesno_ =
true;
683 TASSERT (prjct.children_.size() == 2);
684 FrobnicatorSpecial *special =
dynamic_cast<FrobnicatorSpecial*
> (&prjct.create_lane (
"Special"));
685 special->flags_ = 0xeaeaeaea;
686 special->factor_ = 2.5;
687 special->state_ = -17;
688 TASSERT (prjct.children_.size() == 3);
693 printerr (
"%s\n", streamtext1);
696 FrobnicatorImpl prjct;
697 const bool success =
json_parse (streamtext1, prjct);
699 TASSERT (prjct.children_.size() > 0 &&
dynamic_cast<FrobnicatorImpl*
> (prjct.children_[0])->kind_ ==
"Nidi");
700 TASSERT (prjct.children_.size() > 1 &&
dynamic_cast<FrobnicatorImpl*
> (prjct.children_[1])->kind_ ==
"Odio");
701 TASSERT (prjct.children_.size() > 2 &&
dynamic_cast<FrobnicatorSpecial*
> (prjct.children_[2])->kind_ ==
"Special");
703 FrobnicatorImpl *fimpl =
dynamic_cast<FrobnicatorImpl*
> (prjct.sibling_);
707 printerr (
"%s\n", streamtext2);
708 TASSERT (streamtext1 == streamtext2);
Interface for serializable objects with Reflink support.
virtual void serialize(WritNode &xs)=0
Serialize members and childern.
Central singleton, serves as API entry point.
static ServerP instancep()
Retrieve global Server instance as std::shared_ptr.
One entry in a Writ serialization document.
bool skip_emptystring() const
Omit empty strings during in_save()
WritNode push()
Append new WritNode for serializing arrays during in_save().
bool in_load() const
Return true during deserialization.
bool skip_zero() const
Omit zero integers or floats during in_save()
bool in_save() const
Return true during serialization.
WritNodeS to_nodes()
Create std::vector<WritNode> for serialized arrays during in_load().
void purge_value()
Clean up defaults in Value.
bool loadable(const String &key) const
True if in_load() && has (key).
bool operator&(T &v)
Serialization operator.
Document containing all information needed to serialize and deserialize a Value.
bool in_save() const
Return true during serialization.
static bool typedata_find_minimum(const StringS &typedata, const std::string &fieldname, long double *limit)
Find the minimum value for field of typedata.
static bool typedata_find_maximum(const StringS &typedata, const std::string &fieldname, long double *limit)
Find the maximum value for field of typedata.
static bool typedata_is_loadable(const StringS &typedata, const String &fieldname)
Check for the writable and storage flags in the hints field of typedata.
bool in_load() const
Return true during deserialization.
static bool typedata_is_storable(const StringS &typedata, const String &fieldname)
Check for the readable and storage flags in the hints field of typedata.
Keep track of temporary instances during IpcDispatcher::dispatch_message().
#define assert_return(expr,...)
Return from the current function if expr is unmet and issue an assertion warning.
#define return_unless(cond,...)
Return silently if cond does not evaluate to true with return value ...
#define TEST_INTEGRITY(FUNC)
Register func as an integrity test.
Flags
Flags for allocator behavior.
The Anklang C++ API namespace.
int32_t int32
A 32-bit signed integer.
String name
Names like "bpm", etc.
bool json_parse(const String &jsonstring, T &target)
Parse a well formed JSON string and assign contents to target.
int64_t int64
A 64-bit unsigned integer, use PRI*64 in format strings.
Error
Enum representing Error states.
String json_stringify(const T &source, Writ::Flags flags=Writ::Flags(0))
Create JSON string from source.
Telemetry segment location.
Representation of one possible choice for selection properties.
String warning
Potential problem indicator.
String ident
Identifier used for serialization (may be derived from untranslated label).
String icon
Icon (64x64 pixels) or unicode symbol (possibly wide).
String notice
Additional information of interest.
String blurb
Short description for overviews.
String label
Preferred user interface name.
Value type used to interface with various property types.
int64 as_int() const
Convert Value to int64 or return 0.
void filter(const std::function< bool(const ValueField &)> &pred)
Recursively purge/remove RECORD elements iff to pred (recordfield) == true.
const ValueS & as_array() const
Retrive a non-empty array if Value contains a non-empty array.
Jsonipc wrapper type for objects that support field-wise serialization to/from JSON.
Serializable & set(const char *name, A attribute)
Add a member object pointer.
#define TASSERT(cond)
Unconditional test assertion, enters breakpoint if not fullfilled.