Closed lempiji closed 3 years ago
It is a potentially buggy object structure. It is much better to have distinct arrays for each of the object types: "objectsA"
and "objectsB"
.
Below are two examples: a proxy for a member of an array of algebraic and a proxy as an algebraic wrapper.
/// Proxy type for array of algebraics
unittest
{
import mir.algebraic: Variant;
static struct ObjectA
{
string name;
}
static struct ObjectB
{
double value;
}
alias MyObject = Variant!(ObjectA, ObjectB);
static struct MyObjectArrayProxy
{
MyObject[] array;
this(MyObject[] array) @safe pure nothrow @nogc
{
this.array = array;
}
T opCast(T : MyObject[])()
{
return array;
}
void serialize(S)(ref S serializer) const
{
auto state = serializer.arrayBegin;
foreach (ref e; array)
{
serializer.elemBegin();
// mir.algebraic has builtin support for serialization.
// For other algebraic libraies one can use thier visitor handlers.
serializeValue(serializer, e);
}
serializer.arrayEnd(state);
}
auto deserializeFromAsdf(Asdf asdfData)
{
import asdf : deserializeValue;
import std.traits : EnumMembers;
foreach (e; asdfData.byElement)
{
if (e["name"] != Asdf.init)
{
array ~= MyObject(deserialize!ObjectA(e));
}
else
{
array ~= MyObject(deserialize!ObjectB(e));
}
}
return SerdeException.init;
}
}
static struct SomeObject
{
@serdeProxy!MyObjectArrayProxy MyObject[] objects;
}
string data = q{{"objects":[{"name":"test"},{"value":1.5}]}};
auto value = data.deserialize!SomeObject;
assert (value.serializeToJson == data);
}
/// $(GMREF mir-core, mir, algebraic) with manual serialization.
unittest
{
import asdf.asdf;
static struct Response
{
import mir.algebraic: TaggedVariant;
alias Union = TaggedVariant!(
["double_", "string", "array", "table"],
double,
string,
Response[],
Response[string],
);
Union data;
alias Tag = Union.Kind;
// propogates opEquals, opAssign, and other primitives
alias data this;
static foreach (T; Union.AllowedTypes)
this(T v) @safe pure nothrow @nogc { data = v; }
void serialize(S)(ref S serializer) const
{
import asdf: serializeValue;
import mir.algebraic: visit;
auto o = serializer.objectBegin();
serializer.putKey("tag");
serializer.serializeValue(kind);
serializer.putKey("data");
data.visit!(
(double v) => serializer.serializeValue(v), // specialization for double if required
(const Response[string] v) => serializer.serializeValue(cast(const(Response)[string])v),
(v) => serializer.serializeValue(v),
);
serializer.objectEnd(o);
}
SerdeException deserializeFromAsdf(Asdf asdfData)
{
import asdf : deserializeValue;
import std.traits : EnumMembers;
Tag tag;
if (auto e = asdfData["tag"].deserializeValue(tag))
return e;
final switch (tag)
{
foreach (m; EnumMembers!Tag)
{
case m: {
alias T = Union.AllowedTypes[m];
data = T.init;
if (auto e = asdfData["data"].deserializeValue(data.trustedGet!T))
return e;
break;
}
}
}
return null;
}
}
Response v = 3.0;
assert(v.kind == Response.Tag.double_);
v = "str";
assert(v == "str");
import asdf;
assert(v.serializeToJson == `{"tag":"string","data":"str"}`);
v = Response.init;
v = `{"tag":"array","data":[{"tag":"string","data":"S"}]}`.deserialize!Response;
assert(v.kind == Response.Tag.array);
assert(v.get!(Response[])[0] == "S");
}
Thank you very much!
It seems that what was missing in my implementation was the constructor and opCast.
I omitted too much of the object structure in the example.
A more detailed description of what I want to do is as follows.
// this allow: { "name": "hoge" }
// But I want it to have either type or value.
struct FuzzyParam
{
string name;
@serdeOptional string type;
@serdeOptional string value;
}
/////
struct FixedParam
{
string name;
string value;
}
struct RequiredParam
{
string name;
string type;
}
struct EditableParam
{
string name;
string type;
string value;
}
// this can not allow: { "name": "test" }
// In this implements, I do not miss any combination at all.
alias StrictParam = SumType!(FixedParam, RequiredParam, EditableParam);
I tried to write the following code, but I could not figure out how to implement the Proxy. Does anyone have any good ideas?
code:
data:
I am trying to implement a configuration file with a strict type.