Stroika Library 3.0d18
 
Loading...
Searching...
No Matches
Samples/Serialization/Sources/ObjectVariantMapper.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Foundation/StroikaPreComp.h"
5
10#include "Stroika/Foundation/Containers/Mapping.h"
11#include "Stroika/Foundation/DataExchange/ObjectVariantMapper.h"
19#include "Stroika/Foundation/Streams/MemoryStream.h"
20
21#include "ObjectVariantMapper.h"
22
23using namespace std;
24
25using namespace Stroika::Foundation;
28
29using std::byte;
30
31namespace {
32 void SimpleGettingStarted_ ()
33 {
34 Debug::TraceContextBumper ctx{"SimpleGettingStarted_"};
35
36 // Define some types that you want serialized
37 struct MyType2Serialize1_ {
38 bool fEnabled{false};
39
40 // Not needed to use ObjectVariantMapper - just needed to 'test' if the data round-tripped properly
41 bool operator== (const MyType2Serialize1_& rhs) const = default;
42 };
43
44 // Define an ObjectVariantMapper which knows how to map your types to/from VariantValue objects
46
47 // Add the types to the mapper, which it will need
48 mapper.AddClass<MyType2Serialize1_> ({
49 {"Enabled"sv, &MyType2Serialize1_::fEnabled},
50 });
51
52 // Create a test object to serialize
53 MyType2Serialize1_ tmp;
54 tmp.fEnabled = true;
55
56 /// Map any arbitrary (defined in ObjectVariantMapper) object to a VariantValue
57 VariantValue v = mapper.FromObject (tmp);
58
59 // at this point - we should have VariantValue object with "Enabled" field.
60 // This can then be displayed for debugging purposes using
61 DbgTrace ("v = {}"_f, v);
62
63 // Serialize using any serialization writer defined in Stroika::Foundation::DataExchange::Variant (we chose JSON here)
64 // And dump the results into a temporary memory-based stream
65 Streams::MemoryStream::Ptr<byte> tmpStream = Streams::MemoryStream::New<byte> ();
66 Variant::JSON::Writer{}.Write (v, tmpStream);
67
68 // THEN deserialize, and map back to C++ object form
69 [[maybe_unused]] MyType2Serialize1_ tmp2 = mapper.ToObject<MyType2Serialize1_> (Variant::JSON::Reader{}.Read (tmpStream));
70
71 // make sure new object matches
72 Assert (tmp2 == tmp);
73 }
74}
75
76namespace {
77 void UseObjectVariantMapperTry2_ ()
78 {
79 Debug::TraceContextBumper ctx{"UseObjectVariantMapperTry2_"};
80
81 // Define some types that you want serialized
82 struct SharedContactsConfig_ {
83 bool fEnabled{false};
84 optional<DateTime> fLastSynchronizedAt;
85 Mapping<String, String> fThisPHRsIDToSharedContactID;
86
87 // Not needed to use ObjectVariantMapper - just needed to 'test' if the data round-tripped properly
88 bool operator== (const SharedContactsConfig_& rhs) const = default;
89
90 // Not needed to use ObjectVariantMapper - just to be able to print to debugger/trace-log this type
92 {
94 sb << "{"sv;
95 sb << " Enabled: "sv << fEnabled << ", "sv;
96 sb << " Last-Synchronized-At: "sv << fLastSynchronizedAt << ", "sv;
97 sb << " This-PHRs-ID-To-Shared-Contact-ID: "sv << fThisPHRsIDToSharedContactID;
98 sb << "}"sv;
99 return sb;
100 }
101 };
102
103 // Define an ObjectVariantMapper which knows how to map your types to/from VariantValue objects
104 ObjectVariantMapper mapper;
105
106 // Some types pre-loaded (see @ObjectVariantMapper::ResetToDefaultTypeRegistry)
107 // The rest must be explicitly added to the registry before use.
108
109 mapper.AddClass<SharedContactsConfig_> ({
110 {"Enabled"sv, &SharedContactsConfig_::fEnabled},
111 {"Last-Synchronized-At"sv, &SharedContactsConfig_::fLastSynchronizedAt},
112 {"This-HR-ContactID-To-SharedContactID-Map"sv, &SharedContactsConfig_::fThisPHRsIDToSharedContactID},
113 });
114
115 // fill in a sample object to write
116 SharedContactsConfig_ tmp;
117 {
118 bool newEnabled = true;
119 tmp.fEnabled = newEnabled;
120 tmp.fThisPHRsIDToSharedContactID.Add ("A"sv, "B"sv);
121 tmp.fLastSynchronizedAt = DateTime{1998y / Time::April / 11d, Time::TimeOfDay::Parse ("3pm"sv, locale::classic ())};
122 }
123
124 /// Map that object to a VariantValue
125 VariantValue v = mapper.FromObject (tmp);
126
127 // at this point - we should have VariantValue object with "Enabled", "Last-Synchronized-At" etc fields set properly.
128 // This can then be displayed using
129 DbgTrace ("v = {}"_f, v);
130
131 // Serialize using any serialization writer defined in Stroika::Foundation::DataExchange::Variant (we selected JSON)
132 Streams::MemoryStream::Ptr<byte> tmpStream = Streams::MemoryStream::New<byte> ();
133 Variant::JSON::Writer{}.Write (v, tmpStream);
134
135 // You can persist these to file if you wish
136 constexpr bool kWrite2FileAsWell_ = true;
137 if (kWrite2FileAsWell_) {
138 {
139 IO::FileSystem::FileOutputStream::Ptr tmpFileStream =
140 IO::FileSystem::FileOutputStream::New (IO::FileSystem::WellKnownLocations::GetTemporary () / "t.txt");
141 Variant::JSON::Writer{}.Write (v, tmpFileStream);
142 }
143 {
144 // , and then if you want, try reading it back
146 IO::FileSystem::FileInputStream::New (IO::FileSystem::WellKnownLocations::GetTemporary () / "t.txt");
147 SharedContactsConfig_ tmp2 = mapper.ToObject<SharedContactsConfig_> (Variant::JSON::Reader{}.Read (tmpFileStream));
148 DbgTrace ("tmp2 = {}"_f, tmp2);
149 Assert (tmp2 == tmp);
150 }
151 }
152
153 // THEN deserialize, and map back to C++ object form
154 SharedContactsConfig_ tmp2 = mapper.ToObject<SharedContactsConfig_> (Variant::JSON::Reader{}.Read (tmpStream));
155
156 // and check the roundtrip worked
157 Assert (tmp2 == tmp);
158 }
159}
160
161namespace {
162 void GeneratingReadOnlyFieldsTry3_ ()
163 {
164 Debug::TraceContextBumper ctx{"GeneratingReadOnlyFieldsTry3_"};
165
166 // Define some types that you want serialized
167 struct MyType2Serialize1_ {
168 bool fEnabled{false};
169 };
170
171 // Define an ObjectVariantMapper which knows how to map your types to/from VariantValue objects
172 ObjectVariantMapper mapper;
173
174 // Add the types to the mapper, which it will need
175 mapper.AddClass<MyType2Serialize1_> ({
176 {"Enabled"sv, &MyType2Serialize1_::fEnabled},
177 });
178
179 auto trySerializing = [] (const ObjectVariantMapper& mapper, auto obj) {
180 VariantValue vv = mapper.FromObject (obj);
181
182 // at this point - we should have VariantValue object with "Enabled" field.
183 // This can then be displayed for debugging purposes using
184 DbgTrace ("vv = {}"_f, Characters::ToString (vv));
185
186 // Serialize using any serialization writer defined in Stroika::Foundation::DataExchange::Variant (we chose JSON here)
187 // And dump the results into a temporary memory-based stream
188 Streams::MemoryStream::Ptr<Character> tmpStream = Streams::MemoryStream::New<Character> ();
189 Variant::JSON::Writer{}.Write (vv, tmpStream);
190 using namespace Characters;
191 DbgTrace ("rendered as JSON: = {}"_f, tmpStream.As<String> ().ReplaceAll ("[\n\r]"_RegEx, ""sv));
192 };
193
194 // Create a test object to serialize
195 MyType2Serialize1_ tmp;
196 tmp.fEnabled = true;
197 trySerializing (mapper, tmp);
198
199 // Now a fancier mapper
200 mapper.AddClass<MyType2Serialize1_> (
201 {
202 {"Enabled"sv, &MyType2Serialize1_::fEnabled},
203 },
204 {.fAfterFrom = [] (const ObjectVariantMapper&, const MyType2Serialize1_* objOfType, VariantValue* updateResult) -> void {
206 m.Add ("RandomValue"sv, VariantValue{objOfType->fEnabled ? 2 : 99});
207 *updateResult = VariantValue{m};
208 }});
209 trySerializing (mapper, tmp);
210 }
211}
212
213void Samples::Serialization::ObjectVariantMapper::RunDemo ()
214{
215 SimpleGettingStarted_ ();
216 UseObjectVariantMapperTry2_ ();
217 GeneratingReadOnlyFieldsTry3_ ();
218}
#define DbgTrace
Definition Trace.h:309
Similar to String, but intended to more efficiently construct a String. Mutable type (String is large...
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
nonvirtual String ReplaceAll(const RegularExpression &regEx, const String &with) const
Definition String.cpp:1155
nonvirtual CONTAINER_OF_Key_T As() const
nonvirtual bool Add(ArgByValueType< key_type > key, ArgByValueType< mapped_type > newElt, AddReplaceMode addReplaceMode=AddReplaceMode::eAddReplaces)
Definition Mapping.inl:188
ObjectVariantMapper can be used to map C++ types to and from variant-union types, which can be transp...
nonvirtual void AddClass(const Traversal::Iterable< StructFieldInfo > &fieldDescriptions, const ClassMapperOptions< CLASS > &mapperOptions={})
nonvirtual VariantValue FromObject(const T &from) const
nonvirtual T ToObject(const VariantValue &v) const
nonvirtual void Write(const VariantValue &v, const Streams::OutputStream::Ptr< byte > &out) const
Definition Writer.inl:30
Simple variant-value (case variant union) object, with (variant) basic types analogous to a value in ...
static TimeOfDay Parse(const String &rep, const locale &l=locale{})
String ToString(T &&t, ARGS... args)
Return a debug-friendly, display version of the argument: not guaranteed parsable or usable except fo...
Definition ToString.inl:465
STL namespace.